93803: Refactor existing data services

This commit is contained in:
Yura Bondarenko
2022-08-24 14:53:46 +02:00
parent 8f4b3b58fb
commit 42a2c3c7e2
123 changed files with 2487 additions and 2200 deletions

View File

@@ -13,7 +13,7 @@ import { Item } from '../../../core/shared/item.model';
import { ItemTemplateDataService } from '../../../core/data/item-template-data.service'; import { ItemTemplateDataService } from '../../../core/data/item-template-data.service';
import { Collection } from '../../../core/shared/collection.model'; import { Collection } from '../../../core/shared/collection.model';
import { RequestService } from '../../../core/data/request.service'; import { RequestService } from '../../../core/data/request.service';
import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
import { getCollectionItemTemplateRoute } from '../../collection-page-routing-paths'; import { getCollectionItemTemplateRoute } from '../../collection-page-routing-paths';
describe('CollectionMetadataComponent', () => { describe('CollectionMetadataComponent', () => {
@@ -39,8 +39,8 @@ describe('CollectionMetadataComponent', () => {
const itemTemplateServiceStub = jasmine.createSpyObj('itemTemplateService', { const itemTemplateServiceStub = jasmine.createSpyObj('itemTemplateService', {
findByCollectionID: createSuccessfulRemoteDataObject$(template), findByCollectionID: createSuccessfulRemoteDataObject$(template),
create: createSuccessfulRemoteDataObject$(template), createByCollectionID: createSuccessfulRemoteDataObject$(template),
deleteByCollectionID: observableOf(true), delete: observableOf(true),
getCollectionEndpoint: observableOf(collectionTemplateHref), getCollectionEndpoint: observableOf(collectionTemplateHref),
}); });
@@ -91,12 +91,12 @@ describe('CollectionMetadataComponent', () => {
describe('deleteItemTemplate', () => { describe('deleteItemTemplate', () => {
beforeEach(() => { beforeEach(() => {
(itemTemplateService.deleteByCollectionID as jasmine.Spy).and.returnValue(observableOf(true)); (itemTemplateService.delete as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$({}));
comp.deleteItemTemplate(); comp.deleteItemTemplate();
}); });
it('should call ItemTemplateService.deleteByCollectionID', () => { it('should call ItemTemplateService.delete', () => {
expect(itemTemplateService.deleteByCollectionID).toHaveBeenCalledWith(template, 'collection-id'); expect(itemTemplateService.delete).toHaveBeenCalledWith(template.uuid);
}); });
describe('when delete returns a success', () => { describe('when delete returns a success', () => {
@@ -107,7 +107,7 @@ describe('CollectionMetadataComponent', () => {
describe('when delete returns a failure', () => { describe('when delete returns a failure', () => {
beforeEach(() => { beforeEach(() => {
(itemTemplateService.deleteByCollectionID as jasmine.Spy).and.returnValue(observableOf(false)); (itemTemplateService.delete as jasmine.Spy).and.returnValue(createFailedRemoteDataObject$());
comp.deleteItemTemplate(); comp.deleteItemTemplate();
}); });

View File

@@ -7,12 +7,14 @@ import { ItemTemplateDataService } from '../../../core/data/item-template-data.s
import { combineLatest as combineLatestObservable, Observable } from 'rxjs'; import { combineLatest as combineLatestObservable, Observable } from 'rxjs';
import { RemoteData } from '../../../core/data/remote-data'; import { RemoteData } from '../../../core/data/remote-data';
import { Item } from '../../../core/shared/item.model'; import { Item } from '../../../core/shared/item.model';
import { getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators'; import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators';
import { switchMap } from 'rxjs/operators'; import { map, switchMap } from 'rxjs/operators';
import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { RequestService } from '../../../core/data/request.service'; import { RequestService } from '../../../core/data/request.service';
import { getCollectionItemTemplateRoute } from '../../collection-page-routing-paths'; import { getCollectionItemTemplateRoute } from '../../collection-page-routing-paths';
import { NoContent } from '../../../core/shared/NoContent.model';
import { hasValue } from '../../../shared/empty.util';
/** /**
* Component for editing a collection's metadata * Component for editing a collection's metadata
@@ -65,7 +67,7 @@ export class CollectionMetadataComponent extends ComcolMetadataComponent<Collect
getFirstSucceededRemoteDataPayload(), getFirstSucceededRemoteDataPayload(),
); );
const template$ = collection$.pipe( const template$ = collection$.pipe(
switchMap((collection: Collection) => this.itemTemplateService.create(new Item(), collection.uuid).pipe( switchMap((collection: Collection) => this.itemTemplateService.createByCollectionID(new Item(), collection.uuid).pipe(
getFirstSucceededRemoteDataPayload(), getFirstSucceededRemoteDataPayload(),
)), )),
); );
@@ -83,18 +85,15 @@ export class CollectionMetadataComponent extends ComcolMetadataComponent<Collect
* Delete the item template from the collection * Delete the item template from the collection
*/ */
deleteItemTemplate() { deleteItemTemplate() {
const collection$ = this.dsoRD$.pipe( this.dsoRD$.pipe(
getFirstSucceededRemoteDataPayload(), getFirstSucceededRemoteDataPayload(),
); switchMap((collection: Collection) => this.itemTemplateService.findByCollectionID(collection.uuid)),
const template$ = collection$.pipe( getFirstSucceededRemoteDataPayload(),
switchMap((collection: Collection) => this.itemTemplateService.findByCollectionID(collection.uuid).pipe( switchMap((template) => {
getFirstSucceededRemoteDataPayload(), return this.itemTemplateService.delete(template.uuid);
)), }),
); getFirstCompletedRemoteData(),
combineLatestObservable(collection$, template$).pipe( map((response: RemoteData<NoContent>) => hasValue(response) && response.hasSucceeded),
switchMap(([collection, template]) => {
return this.itemTemplateService.deleteByCollectionID(template, collection.uuid);
})
).subscribe((success: boolean) => { ).subscribe((success: boolean) => {
if (success) { if (success) {
this.notificationsService.success(null, this.translate.get('collection.edit.template.notifications.delete.success')); this.notificationsService.success(null, this.translate.get('collection.edit.template.notifications.delete.success'));

View File

@@ -2,23 +2,26 @@ import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { DSOBreadcrumbsService } from './dso-breadcrumbs.service'; import { DSOBreadcrumbsService } from './dso-breadcrumbs.service';
import { DataService } from '../data/data.service'; import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../shared/operators';
import { getRemoteDataPayload, getFirstCompletedRemoteData } from '../shared/operators';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { DSpaceObject } from '../shared/dspace-object.model'; import { DSpaceObject } from '../shared/dspace-object.model';
import { ChildHALResource } from '../shared/child-hal-resource.model'; import { ChildHALResource } from '../shared/child-hal-resource.model';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { hasValue } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
/** /**
* The class that resolves the BreadcrumbConfig object for a DSpaceObject * The class that resolves the BreadcrumbConfig object for a DSpaceObject
*/ */
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root',
}) })
export abstract class DSOBreadcrumbResolver<T extends ChildHALResource & DSpaceObject> implements Resolve<BreadcrumbConfig<T>> { export abstract class DSOBreadcrumbResolver<T extends ChildHALResource & DSpaceObject> implements Resolve<BreadcrumbConfig<T>> {
constructor(protected breadcrumbService: DSOBreadcrumbsService, protected dataService: DataService<T>) { protected constructor(
protected breadcrumbService: DSOBreadcrumbsService,
protected dataService: IdentifiableDataService<T>,
) {
} }
/** /**
@@ -36,7 +39,7 @@ export abstract class DSOBreadcrumbResolver<T extends ChildHALResource & DSpaceO
if (hasValue(object)) { if (hasValue(object)) {
const fullPath = state.url; const fullPath = state.url;
const url = fullPath.substr(0, fullPath.indexOf(uuid)) + uuid; const url = fullPath.substr(0, fullPath.indexOf(uuid)) + uuid;
return {provider: this.breadcrumbService, key: object, url: url}; return { provider: this.breadcrumbService, key: object, url: url };
} else { } else {
return undefined; return undefined;
} }

View File

@@ -5,15 +5,9 @@ import { FindListOptions } from '../data/find-list-options.model';
describe(`BrowseDefinitionDataService`, () => { describe(`BrowseDefinitionDataService`, () => {
let service: BrowseDefinitionDataService; let service: BrowseDefinitionDataService;
const dataServiceImplSpy = jasmine.createSpyObj('dataService', { const findAllDataSpy = jasmine.createSpyObj('findAllData', {
findAll: EMPTY, findAll: EMPTY,
findByHref: EMPTY,
findAllByHref: EMPTY,
findById: EMPTY,
}); });
const hrefAll = 'https://rest.api/server/api/discover/browses';
const hrefSingle = 'https://rest.api/server/api/discover/browses/author';
const id = 'author';
const options = new FindListOptions(); const options = new FindListOptions();
const linksToFollow = [ const linksToFollow = [
followLink('entries'), followLink('entries'),
@@ -21,35 +15,14 @@ describe(`BrowseDefinitionDataService`, () => {
]; ];
beforeEach(() => { beforeEach(() => {
service = new BrowseDefinitionDataService(null, null, null, null, null, null, null, null); service = new BrowseDefinitionDataService(null, null, null, null);
(service as any).dataService = dataServiceImplSpy; (service as any).findAllData = findAllDataSpy;
}); });
describe(`findAll`, () => { describe(`findAll`, () => {
it(`should call findAll on DataServiceImpl`, () => { it(`should call findAll on findAllData`, () => {
service.findAll(options, true, false, ...linksToFollow); service.findAll(options, true, false, ...linksToFollow);
expect(dataServiceImplSpy.findAll).toHaveBeenCalledWith(options, true, false, ...linksToFollow); expect(findAllDataSpy.findAll).toHaveBeenCalledWith(options, true, false, ...linksToFollow);
});
});
describe(`findByHref`, () => {
it(`should call findByHref on DataServiceImpl`, () => {
service.findByHref(hrefSingle, true, false, ...linksToFollow);
expect(dataServiceImplSpy.findByHref).toHaveBeenCalledWith(hrefSingle, true, false, ...linksToFollow);
});
});
describe(`findAllByHref`, () => {
it(`should call findAllByHref on DataServiceImpl`, () => {
service.findAllByHref(hrefAll, options, true, false, ...linksToFollow);
expect(dataServiceImplSpy.findAllByHref).toHaveBeenCalledWith(hrefAll, options, true, false, ...linksToFollow);
});
});
describe(`findById`, () => {
it(`should call findById on DataServiceImpl`, () => {
service.findAllByHref(id, options, true, false, ...linksToFollow);
expect(dataServiceImplSpy.findAllByHref).toHaveBeenCalledWith(id, options, true, false, ...linksToFollow);
}); });
}); });
}); });

View File

@@ -1,125 +1,40 @@
/* eslint-disable max-classes-per-file */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { BROWSE_DEFINITION } from '../shared/browse-definition.resource-type'; import { BROWSE_DEFINITION } from '../shared/browse-definition.resource-type';
import { DataService } from '../data/data.service';
import { BrowseDefinition } from '../shared/browse-definition.model'; import { BrowseDefinition } from '../shared/browse-definition.model';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
import { PaginatedList } from '../data/paginated-list.model'; import { PaginatedList } from '../data/paginated-list.model';
import { CoreState } from '../core-state.model';
import { FindListOptions } from '../data/find-list-options.model'; import { FindListOptions } from '../data/find-list-options.model';
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
import { FindAllData, FindAllDataImpl } from '../data/base/find-all-data';
class DataServiceImpl extends DataService<BrowseDefinition> {
protected linkPath = 'browses';
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: DefaultChangeAnalyzer<BrowseDefinition>) {
super();
}
}
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root',
}) })
@dataService(BROWSE_DEFINITION) @dataService(BROWSE_DEFINITION)
export class BrowseDefinitionDataService { export class BrowseDefinitionDataService extends IdentifiableDataService<BrowseDefinition> implements FindAllData<BrowseDefinition> {
/** private findAllData: FindAllDataImpl<BrowseDefinition>;
* A private DataService instance to delegate specific methods to.
*/
private dataService: DataServiceImpl;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, ) {
protected http: HttpClient, super('browses', requestService, rdbService, objectCache, halService);
protected comparator: DefaultChangeAnalyzer<BrowseDefinition>) {
this.dataService = new DataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparator); this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
} }
/**
* 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 useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<BrowseDefinition>>>}
* Return an observable that emits object list
*/
findAll(options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<BrowseDefinition>[]): Observable<RemoteData<PaginatedList<BrowseDefinition>>> { findAll(options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<BrowseDefinition>[]): Observable<RemoteData<PaginatedList<BrowseDefinition>>> {
return this.dataService.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Returns an observable of {@link RemoteData} of an {@link BrowseDefinition}, based on an href, with a list of {@link FollowLinkConfig},
* to automatically resolve {@link HALLink}s of the {@link BrowseDefinition}
* @param href The url of {@link BrowseDefinition} we want to retrieve
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
findByHref(href: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<BrowseDefinition>[]): Observable<RemoteData<BrowseDefinition>> {
return this.dataService.findByHref(href, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Returns a list of observables of {@link RemoteData} of {@link BrowseDefinition}s, based on an href, with a list of {@link FollowLinkConfig},
* to automatically resolve {@link HALLink}s of the {@link BrowseDefinition}
* @param href The url of object we want to retrieve
* @param findListOptions Find list options object
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
findAllByHref(href: string, findListOptions: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<BrowseDefinition>[]): Observable<RemoteData<PaginatedList<BrowseDefinition>>> {
return this.dataService.findAllByHref(href, findListOptions, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Returns an observable of {@link RemoteData} of an object, based on its ID, with a list of
* {@link FollowLinkConfig}, to automatically resolve {@link HALLink}s of the object
* @param id ID of object we want to retrieve
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
findById(id: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<BrowseDefinition>[]): Observable<RemoteData<BrowseDefinition>> {
return this.dataService.findById(id, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
} }
} }

View File

@@ -9,6 +9,7 @@ import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-servic
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock'; import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock';
import { FindListOptions } from '../data/find-list-options.model'; import { FindListOptions } from '../data/find-list-options.model';
import { ObjectCacheService } from '../cache/object-cache.service';
const LINK_NAME = 'test'; const LINK_NAME = 'test';
const BROWSE = 'search/findByCollection'; const BROWSE = 'search/findByCollection';
@@ -20,8 +21,10 @@ class TestService extends ConfigService {
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected halService: HALEndpointService) { protected objectCache: ObjectCacheService,
super(requestService, rdbService, null, null, halService, null, null, null, BROWSE); protected halService: HALEndpointService,
) {
super(BROWSE, requestService, rdbService, objectCache, halService);
} }
} }
@@ -45,7 +48,8 @@ describe('ConfigService', () => {
return new TestService( return new TestService(
requestService, requestService,
rdbService, rdbService,
halService null,
halService,
); );
} }

View File

@@ -1,59 +1,14 @@
/* eslint-disable max-classes-per-file */
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { RequestService } from '../data/request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ConfigObject } from './models/config.model'; import { ConfigObject } from './models/config.model';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
import { DataService } from '../data/data.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../cache/object-cache.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { getFirstCompletedRemoteData } from '../shared/operators'; import { getFirstCompletedRemoteData } from '../shared/operators';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { CoreState } from '../core-state.model'; import { BaseDataService } from '../data/base/base-data.service';
class DataServiceImpl extends DataService<ConfigObject> {
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: DefaultChangeAnalyzer<ConfigObject>,
protected linkPath: string
) {
super();
}
}
export abstract class ConfigService {
/**
* A private DataService instance to delegate specific methods to.
*/
private dataService: DataServiceImpl;
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: DefaultChangeAnalyzer<ConfigObject>,
protected linkPath: string
) {
this.dataService = new DataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparator, this.linkPath);
}
export abstract class ConfigService extends BaseDataService<ConfigObject> {
public findByHref(href: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<ConfigObject>[]): Observable<RemoteData<ConfigObject>> { public findByHref(href: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<ConfigObject>[]): Observable<RemoteData<ConfigObject>> {
return this.dataService.findByHref(href, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow).pipe( return super.findByHref(href, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow).pipe(
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
map((rd: RemoteData<ConfigObject>) => { map((rd: RemoteData<ConfigObject>) => {
if (rd.hasFailed) { if (rd.hasFailed) {
@@ -61,7 +16,7 @@ export abstract class ConfigService {
} else { } else {
return rd; return rd;
} }
}) }),
); );
} }
} }

View File

@@ -26,14 +26,10 @@ export class SubmissionAccessesConfigService extends ConfigService {
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DefaultChangeAnalyzer<SubmissionAccessesModel>
) { ) {
super(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparator, 'submissionaccessoptions'); super('submissionaccessoptions', requestService, rdbService, objectCache, halService);
} }
findByHref(href: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow): Observable<RemoteData<SubmissionAccessesModel>> { findByHref(href: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow): Observable<RemoteData<SubmissionAccessesModel>> {

View File

@@ -4,11 +4,7 @@ import { ConfigService } from './config.service';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
import { ConfigObject } from './models/config.model'; import { ConfigObject } from './models/config.model';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { SUBMISSION_FORMS_TYPE } from './models/config-type'; import { SUBMISSION_FORMS_TYPE } from './models/config-type';
@@ -16,7 +12,6 @@ import { SubmissionFormsModel } from './models/config-submission-forms.model';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { CoreState } from '../core-state.model';
@Injectable() @Injectable()
@dataService(SUBMISSION_FORMS_TYPE) @dataService(SUBMISSION_FORMS_TYPE)
@@ -24,14 +19,10 @@ export class SubmissionFormsConfigService extends ConfigService {
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DefaultChangeAnalyzer<SubmissionFormsModel>
) { ) {
super(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparator, 'submissionforms'); super('submissionforms', requestService, rdbService, objectCache, halService);
} }
public findByHref(href: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<SubmissionFormsModel>[]): Observable<RemoteData<SubmissionFormsModel>> { public findByHref(href: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<SubmissionFormsModel>[]): Observable<RemoteData<SubmissionFormsModel>> {

View File

@@ -6,16 +6,11 @@ import { ObjectCacheService } from '../cache/object-cache.service';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { SUBMISSION_UPLOADS_TYPE } from './models/config-type'; import { SUBMISSION_UPLOADS_TYPE } from './models/config-type';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
import { ConfigObject } from './models/config.model'; import { ConfigObject } from './models/config.model';
import { SubmissionUploadsModel } from './models/config-submission-uploads.model'; import { SubmissionUploadsModel } from './models/config-submission-uploads.model';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { CoreState } from '../core-state.model';
/** /**
* Provides methods to retrieve, from REST server, bitstream access conditions configurations applicable during the submission process. * Provides methods to retrieve, from REST server, bitstream access conditions configurations applicable during the submission process.
@@ -26,14 +21,10 @@ export class SubmissionUploadsConfigService extends ConfigService {
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DefaultChangeAnalyzer<SubmissionUploadsModel>
) { ) {
super(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparator, 'submissionuploads'); super('submissionuploads', requestService, rdbService, objectCache, halService);
} }
findByHref(href: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow): Observable<RemoteData<SubmissionUploadsModel>> { findByHref(href: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow): Observable<RemoteData<SubmissionUploadsModel>> {

View File

@@ -178,6 +178,8 @@ import { OrcidHistoryDataService } from './orcid/orcid-history-data.service';
import { OrcidQueue } from './orcid/model/orcid-queue.model'; import { OrcidQueue } from './orcid/model/orcid-queue.model';
import { OrcidHistory } from './orcid/model/orcid-history.model'; import { OrcidHistory } from './orcid/model/orcid-history.model';
import { OrcidAuthService } from './orcid/orcid-auth.service'; import { OrcidAuthService } from './orcid/orcid-auth.service';
import { VocabularyDataService } from './submission/vocabularies/vocabulary.data.service';
import { VocabularyEntryDetailsDataService } from './submission/vocabularies/vocabulary-entry-details.data.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
@@ -300,6 +302,8 @@ const PROVIDERS = [
FilteredDiscoveryPageResponseParsingService, FilteredDiscoveryPageResponseParsingService,
{ provide: NativeWindowService, useFactory: NativeWindowFactory }, { provide: NativeWindowService, useFactory: NativeWindowFactory },
VocabularyService, VocabularyService,
VocabularyDataService,
VocabularyEntryDetailsDataService,
VocabularyTreeviewService, VocabularyTreeviewService,
SequenceService, SequenceService,
GroupDataService, GroupDataService,

View File

@@ -76,6 +76,6 @@ describe('AccessStatusDataService', () => {
}); });
halService = new HALEndpointServiceStub(url); halService = new HALEndpointServiceStub(url);
notificationsService = new NotificationsServiceStub(); notificationsService = new NotificationsServiceStub();
service = new AccessStatusDataService(null, halService, null, notificationsService, objectCache, rdbService, requestService, null); service = new AccessStatusDataService(requestService, rdbService, objectCache, halService);
} }
}); });

View File

@@ -1,38 +1,27 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { DataService } from './data.service';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { CoreState } from '../core-state.model';
import { AccessStatusObject } from 'src/app/shared/object-list/access-status-badge/access-status.model'; import { AccessStatusObject } from 'src/app/shared/object-list/access-status-badge/access-status.model';
import { ACCESS_STATUS } from 'src/app/shared/object-list/access-status-badge/access-status.resource-type'; import { ACCESS_STATUS } from 'src/app/shared/object-list/access-status-badge/access-status.resource-type';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { Item } from '../shared/item.model'; import { Item } from '../shared/item.model';
import { BaseDataService } from './base/base-data.service';
@Injectable() @Injectable()
@dataService(ACCESS_STATUS) @dataService(ACCESS_STATUS)
export class AccessStatusDataService extends DataService<AccessStatusObject> { export class AccessStatusDataService extends BaseDataService<AccessStatusObject> {
protected linkPath = 'accessStatus';
constructor( constructor(
protected comparator: DefaultChangeAnalyzer<AccessStatusObject>,
protected halService: HALEndpointService,
protected http: HttpClient,
protected notificationsService: NotificationsService,
protected objectCache: ObjectCacheService,
protected rdbService: RemoteDataBuildService,
protected requestService: RequestService, protected requestService: RequestService,
protected store: Store<CoreState>, protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
) { ) {
super(); super('accessStatus', requestService, rdbService, objectCache, halService);
} }
/** /**

View File

@@ -47,7 +47,7 @@ describe('BitstreamDataService', () => {
}); });
rdbService = getMockRemoteDataBuildService(); rdbService = getMockRemoteDataBuildService();
service = new BitstreamDataService(requestService, rdbService, null, objectCache, halService, null, null, null, null, bitstreamFormatService); service = new BitstreamDataService(requestService, rdbService, objectCache, halService, null, bitstreamFormatService, null, null);
}); });
describe('when updating the bitstream\'s format', () => { describe('when updating the bitstream\'s format', () => {

View File

@@ -1,10 +1,8 @@
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
import { map, switchMap, take } from 'rxjs/operators'; import { map, switchMap, take } from 'rxjs/operators';
import { hasValue } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
@@ -15,8 +13,6 @@ import { Bundle } from '../shared/bundle.model';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { Item } from '../shared/item.model'; import { Item } from '../shared/item.model';
import { BundleDataService } from './bundle-data.service'; import { BundleDataService } from './bundle-data.service';
import { DataService } from './data.service';
import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
import { buildPaginatedList, PaginatedList } from './paginated-list.model'; import { buildPaginatedList, PaginatedList } from './paginated-list.model';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { PutRequest } from './request.models'; import { PutRequest } from './request.models';
@@ -28,36 +24,43 @@ import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.util
import { PageInfo } from '../shared/page-info.model'; import { PageInfo } from '../shared/page-info.model';
import { RequestParam } from '../cache/models/request-param.model'; import { RequestParam } from '../cache/models/request-param.model';
import { sendRequest } from '../shared/request.operators'; import { sendRequest } from '../shared/request.operators';
import { CoreState } from '../core-state.model';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
import { SearchData, SearchDataImpl } from './base/search-data';
import { PatchData, PatchDataImpl } from './base/patch-data';
import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
import { RestRequestMethod } from './rest-request-method';
import { DeleteData, DeleteDataImpl } from './base/delete-data';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { NoContent } from '../shared/NoContent.model';
import { IdentifiableDataService } from './base/identifiable-data.service';
/** /**
* A service to retrieve {@link Bitstream}s from the REST API * A service to retrieve {@link Bitstream}s from the REST API
*/ */
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root',
}) })
@dataService(BITSTREAM) @dataService(BITSTREAM)
export class BitstreamDataService extends DataService<Bitstream> { export class BitstreamDataService extends IdentifiableDataService<Bitstream> implements SearchData<Bitstream>, PatchData<Bitstream>, DeleteData<Bitstream> {
private searchData: SearchDataImpl<Bitstream>;
/** private patchData: PatchDataImpl<Bitstream>;
* The HAL path to the bitstream endpoint private deleteData: DeleteDataImpl<Bitstream>;
*/
protected linkPath = 'bitstreams';
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DSOChangeAnalyzer<Bitstream>,
protected bundleService: BundleDataService, protected bundleService: BundleDataService,
protected bitstreamFormatService: BitstreamFormatDataService protected bitstreamFormatService: BitstreamFormatDataService,
protected comparator: DSOChangeAnalyzer<Bitstream>,
protected notificationsService: NotificationsService,
) { ) {
super(); super('bitstreams', requestService, rdbService, objectCache, halService);
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
this.patchData = new PatchDataImpl<Bitstream>(this.linkPath, requestService, rdbService, objectCache, halService, comparator, this.responseMsToLive, this.constructIdEndpoint);
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
} }
/** /**
@@ -180,8 +183,89 @@ export class BitstreamDataService extends DataService<Bitstream> {
hrefObs, hrefObs,
useCachedVersionIfAvailable, useCachedVersionIfAvailable,
reRequestOnStale, reRequestOnStale,
...linksToFollow ...linksToFollow,
); );
} }
/**
* Create the HREF for a specific object's search method with given options object
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @return {Observable<string>}
* Return an observable that emits created HREF
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
*/
public getSearchByHref(searchMethod: string, options?: FindListOptions, ...linksToFollow: FollowLinkConfig<Bitstream>[]): Observable<string> {
return this.searchData.getSearchByHref(searchMethod, options, ...linksToFollow);
}
/**
* Make a new FindListRequest with given search method
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>}
* Return an observable that emits response from the server
*/
public searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<Bitstream>[]): Observable<RemoteData<PaginatedList<Bitstream>>> {
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Commit current object changes to the server
* @param method The RestRequestMethod for which de server sync buffer should be committed
*/
public commitUpdates(method?: RestRequestMethod): void {
this.patchData.commitUpdates(method);
}
/**
* Send a patch request for a specified object
* @param {T} object The object to send a patch request for
* @param {Operation[]} operations The patch operations to be performed
*/
public patch(object: Bitstream, operations: []): Observable<RemoteData<Bitstream>> {
return this.patchData.patch(object, operations);
}
/**
* Add a new patch to the object cache
* The patch is derived from the differences between the given object and its version in the object cache
* @param {DSpaceObject} object The given object
*/
public update(object: Bitstream): Observable<RemoteData<Bitstream>> {
return this.patchData.update(object);
}
/**
* Delete an existing object on the server
* @param objectId The id of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
*/
delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.delete(objectId, copyVirtualMetadata);
}
/**
* Delete an existing object on the server
* @param href The self link of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
* Only emits once all request related to the DSO has been invalidated.
*/
deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
}
} }

View File

@@ -50,8 +50,6 @@ describe('BitstreamFormatDataService', () => {
} as HALEndpointService; } as HALEndpointService;
const notificationsService = {} as NotificationsService; const notificationsService = {} as NotificationsService;
const http = {} as HttpClient;
const comparator = {} as any;
let rd; let rd;
let rdbService: RemoteDataBuildService; let rdbService: RemoteDataBuildService;
@@ -65,12 +63,10 @@ describe('BitstreamFormatDataService', () => {
return new BitstreamFormatDataService( return new BitstreamFormatDataService(
requestService, requestService,
rdbService, rdbService,
store,
objectCache, objectCache,
halService, halService,
notificationsService, notificationsService,
http, store,
comparator
); );
} }

View File

@@ -1,13 +1,8 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { createSelector, select, Store } from '@ngrx/store'; import { createSelector, select, Store } from '@ngrx/store';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { distinctUntilChanged, map, tap } from 'rxjs/operators'; import { distinctUntilChanged, map, tap } from 'rxjs/operators';
import { import { BitstreamFormatsRegistryDeselectAction, BitstreamFormatsRegistryDeselectAllAction, BitstreamFormatsRegistrySelectAction } from '../../admin/admin-registries/bitstream-formats/bitstream-format.actions';
BitstreamFormatsRegistryDeselectAction,
BitstreamFormatsRegistryDeselectAllAction,
BitstreamFormatsRegistrySelectAction
} from '../../admin/admin-registries/bitstream-formats/bitstream-format.actions';
import { BitstreamFormatRegistryState } from '../../admin/admin-registries/bitstream-formats/bitstream-format.reducers'; import { BitstreamFormatRegistryState } from '../../admin/admin-registries/bitstream-formats/bitstream-format.reducers';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
@@ -18,40 +13,52 @@ import { BitstreamFormat } from '../shared/bitstream-format.model';
import { BITSTREAM_FORMAT } from '../shared/bitstream-format.resource-type'; import { BITSTREAM_FORMAT } from '../shared/bitstream-format.resource-type';
import { Bitstream } from '../shared/bitstream.model'; import { Bitstream } from '../shared/bitstream.model';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { DataService } from './data.service';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { PostRequest, PutRequest } from './request.models'; import { PostRequest, PutRequest } from './request.models';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { sendRequest } from '../shared/request.operators'; import { sendRequest } from '../shared/request.operators';
import { CoreState } from '../core-state.model'; import { CoreState } from '../core-state.model';
import { IdentifiableDataService } from './base/identifiable-data.service';
import { DeleteData, DeleteDataImpl } from './base/delete-data';
import { FindAllData, FindAllDataImpl } from './base/find-all-data';
import { FollowLinkConfig } from 'src/app/shared/utils/follow-link-config.model';
import { FindListOptions } from './find-list-options.model';
import { PaginatedList } from './paginated-list.model';
import { NoContent } from '../shared/NoContent.model';
const bitstreamFormatsStateSelector = createSelector( const bitstreamFormatsStateSelector = createSelector(
coreSelector, coreSelector,
(state: CoreState) => state.bitstreamFormats (state: CoreState) => state.bitstreamFormats,
);
const selectedBitstreamFormatSelector = createSelector(
bitstreamFormatsStateSelector,
(bitstreamFormatRegistryState: BitstreamFormatRegistryState) => bitstreamFormatRegistryState.selectedBitstreamFormats,
); );
const selectedBitstreamFormatSelector = createSelector(bitstreamFormatsStateSelector,
(bitstreamFormatRegistryState: BitstreamFormatRegistryState) => bitstreamFormatRegistryState.selectedBitstreamFormats);
/** /**
* A service responsible for fetching/sending data from/to the REST API on the bitstreamformats endpoint * A service responsible for fetching/sending data from/to the REST API on the bitstreamformats endpoint
*/ */
@Injectable() @Injectable()
@dataService(BITSTREAM_FORMAT) @dataService(BITSTREAM_FORMAT)
export class BitstreamFormatDataService extends DataService<BitstreamFormat> { export class BitstreamFormatDataService extends IdentifiableDataService<BitstreamFormat> implements FindAllData<BitstreamFormat>, DeleteData<BitstreamFormat> {
protected linkPath = 'bitstreamformats'; protected linkPath = 'bitstreamformats';
private findAllData: FindAllDataImpl<BitstreamFormat>;
private deleteData: DeleteDataImpl<BitstreamFormat>;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected http: HttpClient, protected store: Store<CoreState>,
protected comparator: DefaultChangeAnalyzer<BitstreamFormat>) { ) {
super(); super('bitstreamformats', requestService, rdbService, objectCache, halService);
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
} }
/** /**
@@ -60,7 +67,7 @@ export class BitstreamFormatDataService extends DataService<BitstreamFormat> {
*/ */
public getUpdateEndpoint(formatId: string): Observable<string> { public getUpdateEndpoint(formatId: string): Observable<string> {
return this.getBrowseEndpoint().pipe( return this.getBrowseEndpoint().pipe(
map((endpoint: string) => this.getIDHref(endpoint, formatId)) map((endpoint: string) => this.getIDHref(endpoint, formatId)),
); );
} }
@@ -147,4 +154,47 @@ export class BitstreamFormatDataService extends DataService<BitstreamFormat> {
findByBitstream(bitstream: Bitstream): Observable<RemoteData<BitstreamFormat>> { findByBitstream(bitstream: Bitstream): Observable<RemoteData<BitstreamFormat>> {
return this.findByHref(bitstream._links.format.href); return this.findByHref(bitstream._links.format.href);
} }
/**
* 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 useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>>}
* Return an observable that emits object list
*/
findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<BitstreamFormat>[]): Observable<RemoteData<PaginatedList<BitstreamFormat>>> {
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Delete an existing object on the server
* @param objectId The id of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
*/
delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.delete(objectId, copyVirtualMetadata);
}
/**
* Delete an existing object on the server
* @param href The self link of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
* Only emits once all request related to the DSO has been invalidated.
*/
deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
}
} }

View File

@@ -64,9 +64,6 @@ describe('BundleDataService', () => {
store, store,
objectCache, objectCache,
halService, halService,
notificationsService,
http,
comparator,
); );
} }

View File

@@ -1,10 +1,7 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, switchMap, take } from 'rxjs/operators'; import { map, switchMap, take } from 'rxjs/operators';
import { hasValue } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
@@ -13,8 +10,6 @@ import { Bundle } from '../shared/bundle.model';
import { BUNDLE } from '../shared/bundle.resource-type'; import { BUNDLE } from '../shared/bundle.resource-type';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { Item } from '../shared/item.model'; import { Item } from '../shared/item.model';
import { DataService } from './data.service';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { PaginatedList } from './paginated-list.model'; import { PaginatedList } from './paginated-list.model';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { GetRequest } from './request.models'; import { GetRequest } from './request.models';
@@ -22,30 +17,35 @@ import { RequestService } from './request.service';
import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model';
import { Bitstream } from '../shared/bitstream.model'; import { Bitstream } from '../shared/bitstream.model';
import { RequestEntryState } from './request-entry-state.model'; import { RequestEntryState } from './request-entry-state.model';
import { CoreState } from '../core-state.model';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
import { IdentifiableDataService } from './base/identifiable-data.service';
import { PatchData, PatchDataImpl } from './base/patch-data';
import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
import { RestRequestMethod } from './rest-request-method';
import { Operation } from 'fast-json-patch';
/** /**
* A service to retrieve {@link Bundle}s from the REST API * A service to retrieve {@link Bundle}s from the REST API
*/ */
@Injectable( @Injectable(
{providedIn: 'root'} { providedIn: 'root' },
) )
@dataService(BUNDLE) @dataService(BUNDLE)
export class BundleDataService extends DataService<Bundle> { export class BundleDataService extends IdentifiableDataService<Bundle> implements PatchData<Bundle> {
protected linkPath = 'bundles'; private bitstreamsEndpoint = 'bitstreams';
protected bitstreamsEndpoint = 'bitstreams';
private patchData: PatchDataImpl<Bundle>;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, protected comparator: DSOChangeAnalyzer<Bundle>,
protected http: HttpClient, ) {
protected comparator: DefaultChangeAnalyzer<Bundle>) { super('bundles', requestService, rdbService, objectCache, halService);
super();
this.patchData = new PatchDataImpl<Bundle>(this.linkPath, requestService, rdbService, objectCache, halService, comparator, this.responseMsToLive, this.constructIdEndpoint);
} }
/** /**
@@ -133,7 +133,7 @@ export class BundleDataService extends DataService<Bundle> {
const hrefObs = this.getBitstreamsEndpoint(bundleId, searchOptions); const hrefObs = this.getBitstreamsEndpoint(bundleId, searchOptions);
hrefObs.pipe( hrefObs.pipe(
take(1) take(1),
).subscribe((href) => { ).subscribe((href) => {
const request = new GetRequest(this.requestService.generateRequestId(), href); const request = new GetRequest(this.requestService.generateRequestId(), href);
this.requestService.send(request, true); this.requestService.send(request, true);
@@ -141,4 +141,30 @@ export class BundleDataService extends DataService<Bundle> {
return this.rdbService.buildList<Bitstream>(hrefObs, ...linksToFollow); return this.rdbService.buildList<Bitstream>(hrefObs, ...linksToFollow);
} }
/**
* Commit current object changes to the server
* @param method The RestRequestMethod for which de server sync buffer should be committed
*/
public commitUpdates(method?: RestRequestMethod): void {
this.patchData.commitUpdates(method);
}
/**
* Send a patch request for a specified object
* @param {T} object The object to send a patch request for
* @param {Operation[]} operations The patch operations to be performed
*/
public patch(object: Bundle, operations: Operation[]): Observable<RemoteData<Bundle>> {
return this.patchData.patch(object, operations);
}
/**
* Add a new patch to the object cache
* The patch is derived from the differences between the given object and its version in the object cache
* @param {DSpaceObject} object The given object
*/
public update(object: Bundle): Observable<RemoteData<Bundle>> {
return this.patchData.update(object);
}
} }

View File

@@ -201,7 +201,7 @@ describe('CollectionDataService', () => {
notificationsService = new NotificationsServiceStub(); notificationsService = new NotificationsServiceStub();
translate = getMockTranslateService(); translate = getMockTranslateService();
service = new CollectionDataService(requestService, rdbService, null, null, objectCache, halService, notificationsService, null, null, null, translate); service = new CollectionDataService(requestService, rdbService, objectCache, halService, null, notificationsService, null, null, translate);
} }
}); });

View File

@@ -1,6 +1,5 @@
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { filter, map, switchMap, take } from 'rxjs/operators'; import { filter, map, switchMap, take } from 'rxjs/operators';
@@ -33,30 +32,27 @@ import {
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { BitstreamDataService } from './bitstream-data.service'; import { BitstreamDataService } from './bitstream-data.service';
import { RestRequest } from './rest-request.model'; import { RestRequest } from './rest-request.model';
import { CoreState } from '../core-state.model';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
import { Community } from '../shared/community.model';
@Injectable() @Injectable()
@dataService(COLLECTION) @dataService(COLLECTION)
export class CollectionDataService extends ComColDataService<Collection> { export class CollectionDataService extends ComColDataService<Collection> {
protected linkPath = 'collections';
protected errorTitle = 'collection.source.update.notifications.error.title'; protected errorTitle = 'collection.source.update.notifications.error.title';
protected contentSourceError = 'collection.source.update.notifications.error.content'; protected contentSourceError = 'collection.source.update.notifications.error.content';
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected cds: CommunityDataService,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected comparator: DSOChangeAnalyzer<Community>,
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected http: HttpClient,
protected bitstreamDataService: BitstreamDataService, protected bitstreamDataService: BitstreamDataService,
protected comparator: DSOChangeAnalyzer<Collection>, protected communityDataService: CommunityDataService,
protected translate: TranslateService protected translate: TranslateService,
) { ) {
super(); super('collections', requestService, rdbService, objectCache, halService, comparator, notificationsService, bitstreamDataService);
} }
/** /**
@@ -289,10 +285,10 @@ export class CollectionDataService extends ComColDataService<Collection> {
protected getScopeCommunityHref(options: FindListOptions) { protected getScopeCommunityHref(options: FindListOptions) {
return this.cds.getEndpoint().pipe( return this.communityDataService.getEndpoint().pipe(
map((endpoint: string) => this.cds.getIDHref(endpoint, options.scopeID)), map((endpoint: string) => this.communityDataService.getIDHref(endpoint, options.scopeID)),
filter((href: string) => isNotEmpty(href)), filter((href: string) => isNotEmpty(href)),
take(1) take(1),
); );
} }
} }

View File

@@ -47,7 +47,7 @@ class TestService extends ComColDataService<any> {
protected comparator: DSOChangeAnalyzer<Community>, protected comparator: DSOChangeAnalyzer<Community>,
protected linkPath: string protected linkPath: string
) { ) {
super(); super('something', requestService, rdbService, objectCache, halService, comparator, notificationsService, bitstreamDataService);
} }
protected getFindByParentHref(parentUUID: string): Observable<string> { protected getFindByParentHref(parentUUID: string): Observable<string> {

View File

@@ -4,7 +4,6 @@ import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { Community } from '../shared/community.model'; import { Community } from '../shared/community.model';
import { HALLink } from '../shared/hal-link.model'; import { HALLink } from '../shared/hal-link.model';
import { DataService } from './data.service';
import { PaginatedList } from './paginated-list.model'; import { PaginatedList } from './paginated-list.model';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
@@ -17,11 +16,44 @@ import { createFailedRemoteDataObject$ } from '../../shared/remote-data.utils';
import { URLCombiner } from '../url-combiner/url-combiner'; import { URLCombiner } from '../url-combiner/url-combiner';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
import { IdentifiableDataService } from './base/identifiable-data.service';
import { PatchData, PatchDataImpl } from './base/patch-data';
import { DeleteData, DeleteDataImpl } from './base/delete-data';
import { FindAllData, FindAllDataImpl } from './base/find-all-data';
import { SearchData, SearchDataImpl } from './base/search-data';
import { RestRequestMethod } from './rest-request-method';
import { CreateData, CreateDataImpl } from './base/create-data';
import { RequestParam } from '../cache/models/request-param.model';
import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
export abstract class ComColDataService<T extends Community | Collection> extends DataService<T> { export abstract class ComColDataService<T extends Community | Collection> extends IdentifiableDataService<T> implements CreateData<T>, FindAllData<T>, SearchData<T>, PatchData<T>, DeleteData<T> {
protected abstract objectCache: ObjectCacheService; private createData: CreateData<T>;
protected abstract halService: HALEndpointService; private findAllData: FindAllData<T>;
protected abstract bitstreamDataService: BitstreamDataService; private searchData: SearchData<T>;
private patchData: PatchData<T>;
private deleteData: DeleteData<T>;
protected constructor(
protected linkPath: string,
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected comparator: DSOChangeAnalyzer<T>,
protected notificationsService: NotificationsService,
protected bitstreamDataService: BitstreamDataService,
) {
super(linkPath, requestService, rdbService, objectCache, halService);
this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive);
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
this.searchData = new SearchDataImpl<T>(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
this.patchData = new PatchDataImpl<T>(this.linkPath, requestService, rdbService, objectCache, halService, comparator, this.responseMsToLive, this.constructIdEndpoint);
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
}
/** /**
* Get the scoped endpoint URL by fetching the object with * Get the scoped endpoint URL by fetching the object with
@@ -129,4 +161,128 @@ export abstract class ComColDataService<T extends Community | Collection> extend
const parentCommunity = dso._links.parentCommunity; const parentCommunity = dso._links.parentCommunity;
return isNotEmpty(parentCommunity) ? parentCommunity.href : null; return isNotEmpty(parentCommunity) ? parentCommunity.href : null;
} }
/**
* Create a new object on the server, and store the response in the object cache
*
* @param object The object to create
* @param params Array with additional params to combine with query string
*/
create(object: T, ...params: RequestParam[]): Observable<RemoteData<T>> {
return this.createData.create(object, ...params);
}
/**
* 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 useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>>}
* Return an observable that emits object list
*/
public findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<T>[]): Observable<RemoteData<PaginatedList<T>>> {
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Create the HREF with given options object
*
* @param options The [[FindListOptions]] object
* @param linkPath The link path for the object
* @return {Observable<string>}
* Return an observable that emits created HREF
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
*/
public getFindAllHref(options?: FindListOptions, linkPath?: string, ...linksToFollow: FollowLinkConfig<T>[]): Observable<string> {
return this.findAllData.getFindAllHref(options, linkPath, ...linksToFollow);
}
/**
* Create the HREF for a specific object's search method with given options object
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @return {Observable<string>}
* Return an observable that emits created HREF
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
*/
public getSearchByHref(searchMethod: string, options?: FindListOptions, ...linksToFollow: FollowLinkConfig<T>[]): Observable<string> {
return this.searchData.getSearchByHref(searchMethod, options, ...linksToFollow);
}
/**
* Make a new FindListRequest with given search method
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>}
* Return an observable that emits response from the server
*/
public searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<T>[]): Observable<RemoteData<PaginatedList<T>>> {
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Commit current object changes to the server
* @param method The RestRequestMethod for which de server sync buffer should be committed
*/
public commitUpdates(method?: RestRequestMethod): void {
this.patchData.commitUpdates(method);
}
/**
* Send a patch request for a specified object
* @param {T} object The object to send a patch request for
* @param {Operation[]} operations The patch operations to be performed
*/
public patch(object: T, operations: []): Observable<RemoteData<T>> {
return this.patchData.patch(object, operations);
}
/**
* Add a new patch to the object cache
* The patch is derived from the differences between the given object and its version in the object cache
* @param {DSpaceObject} object The given object
*/
public update(object: T): Observable<RemoteData<T>> {
return this.patchData.update(object);
}
/**
* Delete an existing object on the server
* @param objectId The id of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
*/
public delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.delete(objectId, copyVirtualMetadata);
}
/**
* Delete an existing object on the server
* @param href The self link of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
* Only emits once all request related to the DSO has been invalidated.
*/
public deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
}
} }

View File

@@ -1,7 +1,5 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { filter, map, switchMap, take } from 'rxjs/operators'; import { filter, map, switchMap, take } from 'rxjs/operators';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
@@ -19,27 +17,23 @@ import { RequestService } from './request.service';
import { BitstreamDataService } from './bitstream-data.service'; import { BitstreamDataService } from './bitstream-data.service';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { isNotEmpty } from '../../shared/empty.util'; import { isNotEmpty } from '../../shared/empty.util';
import { CoreState } from '../core-state.model';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
@Injectable() @Injectable()
@dataService(COMMUNITY) @dataService(COMMUNITY)
export class CommunityDataService extends ComColDataService<Community> { export class CommunityDataService extends ComColDataService<Community> {
protected linkPath = 'communities';
protected topLinkPath = 'search/top'; protected topLinkPath = 'search/top';
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected comparator: DSOChangeAnalyzer<Community>,
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected bitstreamDataService: BitstreamDataService, protected bitstreamDataService: BitstreamDataService,
protected http: HttpClient,
protected comparator: DSOChangeAnalyzer<Community>
) { ) {
super(); super('communities', requestService, rdbService, objectCache, halService, comparator, notificationsService, bitstreamDataService);
} }
getEndpoint() { getEndpoint() {

View File

@@ -5,8 +5,6 @@ import { HALEndpointService } from '../shared/hal-endpoint.service';
import { GetRequest } from './request.models'; import { GetRequest } from './request.models';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { ConfigurationDataService } from './configuration-data.service'; import { ConfigurationDataService } from './configuration-data.service';
import { ConfigurationProperty } from '../shared/configuration-property.model'; import { ConfigurationProperty } from '../shared/configuration-property.model';
@@ -44,18 +42,12 @@ describe('ConfigurationDataService', () => {
}) })
}); });
objectCache = {} as ObjectCacheService; objectCache = {} as ObjectCacheService;
const notificationsService = {} as NotificationsService;
const http = {} as HttpClient;
const comparator = {} as any;
service = new ConfigurationDataService( service = new ConfigurationDataService(
requestService, requestService,
rdbService, rdbService,
objectCache, objectCache,
halService, halService,
notificationsService,
http,
comparator
); );
}); });

View File

@@ -1,55 +1,30 @@
/* eslint-disable max-classes-per-file */ /* eslint-disable max-classes-per-file */
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { DataService } from './data.service';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { ConfigurationProperty } from '../shared/configuration-property.model'; import { ConfigurationProperty } from '../shared/configuration-property.model';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { CONFIG_PROPERTY } from '../shared/config-property.resource-type'; import { CONFIG_PROPERTY } from '../shared/config-property.resource-type';
import { CoreState } from '../core-state.model'; import { IdentifiableDataService } from './base/identifiable-data.service';
class DataServiceImpl extends DataService<ConfigurationProperty> {
protected linkPath = 'properties';
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: DefaultChangeAnalyzer<ConfigurationProperty>) {
super();
}
}
@Injectable() @Injectable()
@dataService(CONFIG_PROPERTY) @dataService(CONFIG_PROPERTY)
/** /**
* Data Service responsible for retrieving Configuration properties * Data Service responsible for retrieving Configuration properties
*/ */
export class ConfigurationDataService { export class ConfigurationDataService extends IdentifiableDataService<ConfigurationProperty> {
protected linkPath = 'properties';
private dataService: DataServiceImpl;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, ) {
protected http: HttpClient, super('properties', requestService, rdbService, objectCache, halService);
protected comparator: DefaultChangeAnalyzer<ConfigurationProperty>) {
this.dataService = new DataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparator);
} }
/** /**
@@ -57,6 +32,6 @@ export class ConfigurationDataService {
* @param name * @param name
*/ */
findByPropertyName(name: string): Observable<RemoteData<ConfigurationProperty>> { findByPropertyName(name: string): Observable<RemoteData<ConfigurationProperty>> {
return this.dataService.findById(name); return this.findById(name);
} }
} }

View File

@@ -1,22 +1,18 @@
import { HttpClient } from '@angular/common/http';
import { Store } from '@ngrx/store';
import { cold, getTestScheduler } from 'jasmine-marbles'; import { cold, getTestScheduler } from 'jasmine-marbles';
import { TestScheduler } from 'rxjs/testing'; import { TestScheduler } from 'rxjs/testing';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { followLink } from '../../shared/utils/follow-link-config.model'; import { followLink } from '../../shared/utils/follow-link-config.model';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { DsoRedirectDataService } from './dso-redirect-data.service'; import { DsoRedirectService } from './dso-redirect.service';
import { GetRequest, IdentifierType } from './request.models'; import { GetRequest, IdentifierType } from './request.models';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
import { Item } from '../shared/item.model'; import { Item } from '../shared/item.model';
import { CoreState } from '../core-state.model';
describe('DsoRedirectDataService', () => { describe('DsoRedirectService', () => {
let scheduler: TestScheduler; let scheduler: TestScheduler;
let service: DsoRedirectDataService; let service: DsoRedirectService;
let halService: HALEndpointService; let halService: HALEndpointService;
let requestService: RequestService; let requestService: RequestService;
let rdbService: RemoteDataBuildService; let rdbService: RemoteDataBuildService;
@@ -29,10 +25,6 @@ describe('DsoRedirectDataService', () => {
const requestHandleURL = `https://rest.api/rest/api/pid/find?id=${encodedHandle}`; const requestHandleURL = `https://rest.api/rest/api/pid/find?id=${encodedHandle}`;
const requestUUIDURL = `https://rest.api/rest/api/pid/find?id=${dsoUUID}`; const requestUUIDURL = `https://rest.api/rest/api/pid/find?id=${dsoUUID}`;
const requestUUID = '34cfed7c-f597-49ef-9cbe-ea351f0023c2'; const requestUUID = '34cfed7c-f597-49ef-9cbe-ea351f0023c2';
const store = {} as Store<CoreState>;
const notificationsService = {} as NotificationsService;
const http = {} as HttpClient;
const comparator = {} as any;
const objectCache = {} as ObjectCacheService; const objectCache = {} as ObjectCacheService;
beforeEach(() => { beforeEach(() => {
@@ -59,20 +51,16 @@ describe('DsoRedirectDataService', () => {
a: remoteData a: remoteData
}) })
}); });
service = new DsoRedirectDataService( service = new DsoRedirectService(
requestService, requestService,
rdbService, rdbService,
store,
objectCache, objectCache,
halService, halService,
notificationsService, router,
http,
comparator,
router
); );
}); });
describe('findById', () => { describe('findByIdAndIDType', () => {
it('should call HALEndpointService with the path to the pid endpoint', () => { it('should call HALEndpointService with the path to the pid endpoint', () => {
scheduler.schedule(() => service.findByIdAndIDType(dsoHandle, IdentifierType.HANDLE)); scheduler.schedule(() => service.findByIdAndIDType(dsoHandle, IdentifierType.HANDLE));
scheduler.flush(); scheduler.flush();
@@ -141,7 +129,7 @@ describe('DsoRedirectDataService', () => {
redir.subscribe(); redir.subscribe();
scheduler.schedule(() => redir); scheduler.schedule(() => redir);
scheduler.flush(); scheduler.flush();
expect(router.navigate).toHaveBeenCalledWith([remoteData.payload.type + 's/' + remoteData.payload.uuid]); expect(router.navigate).toHaveBeenCalledWith(['/collections/' + remoteData.payload.uuid]);
}); });
it('should navigate to communities route', () => { it('should navigate to communities route', () => {
@@ -150,55 +138,58 @@ describe('DsoRedirectDataService', () => {
redir.subscribe(); redir.subscribe();
scheduler.schedule(() => redir); scheduler.schedule(() => redir);
scheduler.flush(); scheduler.flush();
expect(router.navigate).toHaveBeenCalledWith(['communities/' + remoteData.payload.uuid]); expect(router.navigate).toHaveBeenCalledWith(['/communities/' + remoteData.payload.uuid]);
}); });
}); });
describe('getIDHref', () => { describe('DataService', () => { // todo: should only test the id/uuid interpolation thingy
it('should return endpoint', () => { describe('getIDHref', () => { // todo: should be able to move this up to IdentifiableDataService?
const result = (service as any).getIDHref(pidLink, dsoUUID); it('should return endpoint', () => {
expect(result).toEqual(requestUUIDURL); const result = (service as any).dataService.getIDHref(pidLink, dsoUUID);
}); expect(result).toEqual(requestUUIDURL);
});
it('should include single linksToFollow as embed', () => { it('should include single linksToFollow as embed', () => {
const expected = `${requestUUIDURL}&embed=bundles`; const expected = `${requestUUIDURL}&embed=bundles`;
const result = (service as any).getIDHref(pidLink, dsoUUID, followLink('bundles')); const result = (service as any).dataService.getIDHref(pidLink, dsoUUID, followLink('bundles'));
expect(result).toEqual(expected); expect(result).toEqual(expected);
}); });
it('should include multiple linksToFollow as embed', () => { it('should include multiple linksToFollow as embed', () => {
const expected = `${requestUUIDURL}&embed=bundles&embed=owningCollection&embed=templateItemOf`; const expected = `${requestUUIDURL}&embed=bundles&embed=owningCollection&embed=templateItemOf`;
const result = (service as any).getIDHref(pidLink, dsoUUID, followLink('bundles'), followLink('owningCollection'), followLink('templateItemOf')); const result = (service as any).dataService.getIDHref(pidLink, dsoUUID, followLink('bundles'), followLink('owningCollection'), followLink('templateItemOf'));
expect(result).toEqual(expected); expect(result).toEqual(expected);
}); });
it('should not include linksToFollow with shouldEmbed = false', () => { it('should not include linksToFollow with shouldEmbed = false', () => {
const expected = `${requestUUIDURL}&embed=templateItemOf`; const expected = `${requestUUIDURL}&embed=templateItemOf`;
const result = (service as any).getIDHref( const result = (service as any).dataService.getIDHref(
pidLink, pidLink,
dsoUUID, dsoUUID,
followLink('bundles', { shouldEmbed: false }), followLink('bundles', { shouldEmbed: false }),
followLink('owningCollection', { shouldEmbed: false }), followLink('owningCollection', { shouldEmbed: false }),
followLink('templateItemOf') followLink('templateItemOf'),
); );
expect(result).toEqual(expected); expect(result).toEqual(expected);
}); });
it('should include nested linksToFollow 3lvl', () => { it('should include nested linksToFollow 3lvl', () => {
const expected = `${requestUUIDURL}&embed=owningCollection/itemtemplate/relationships`; const expected = `${requestUUIDURL}&embed=owningCollection/itemtemplate/relationships`;
const result = (service as any).getIDHref( const result = (service as any).dataService.getIDHref(
pidLink, pidLink,
dsoUUID, dsoUUID,
followLink('owningCollection', followLink(
{}, 'owningCollection',
followLink('itemtemplate',
{}, {},
followLink('relationships') followLink(
) 'itemtemplate',
) {},
); followLink('relationships'),
expect(result).toEqual(expected); ),
),
);
expect(result).toEqual(expected);
});
}); });
}); });
}); });

View File

@@ -1,95 +0,0 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { hasValue } from '../../shared/empty.util';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { DataService } from './data.service';
import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
import { RemoteData } from './remote-data';
import { IdentifierType } from './request.models';
import { RequestService } from './request.service';
import { getFirstCompletedRemoteData } from '../shared/operators';
import { DSpaceObject } from '../shared/dspace-object.model';
import { Item } from '../shared/item.model';
import { getItemPageRoute } from '../../item-page/item-page-routing-paths';
import { CoreState } from '../core-state.model';
@Injectable()
export class DsoRedirectDataService extends DataService<any> {
// Set the default link path to the identifier lookup endpoint.
protected linkPath = 'pid';
private uuidEndpoint = 'dso';
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: DSOChangeAnalyzer<any>,
private router: Router) {
super();
}
setLinkPath(identifierType: IdentifierType) {
// The default 'pid' endpoint for identifiers does not support uuid lookups.
// For uuid lookups we need to change the linkPath.
if (identifierType === IdentifierType.UUID) {
this.linkPath = this.uuidEndpoint;
}
}
getIDHref(endpoint, resourceID, ...linksToFollow: FollowLinkConfig<any>[]): string {
// Supporting both identifier (pid) and uuid (dso) endpoints
return this.buildHrefFromFindOptions( endpoint.replace(/\{\?id\}/, `?id=${resourceID}`)
.replace(/\{\?uuid\}/, `?uuid=${resourceID}`),
{}, [], ...linksToFollow);
}
findByIdAndIDType(id: string, identifierType = IdentifierType.UUID): Observable<RemoteData<DSpaceObject>> {
this.setLinkPath(identifierType);
return this.findById(id).pipe(
getFirstCompletedRemoteData(),
tap((response) => {
if (response.hasSucceeded) {
const dso = response.payload;
const uuid = dso.uuid;
if (hasValue(uuid)) {
let newRoute = this.getEndpointFromDSOType(response.payload.type);
if (dso.type.startsWith('item')) {
newRoute = getItemPageRoute(dso as Item);
} else if (hasValue(newRoute)) {
newRoute += '/' + uuid;
}
if (hasValue(newRoute)) {
this.router.navigate([newRoute]);
}
}
}
})
);
}
// Is there an existing method somewhere else that converts dso type to route?
getEndpointFromDSOType(dsoType: string): string {
// Are there other types to consider?
if (dsoType.startsWith('item')) {
return 'items';
} else if (dsoType.startsWith('community')) {
return 'communities';
} else if (dsoType.startsWith('collection')) {
return 'collections';
} else {
return '';
}
}
}

View File

@@ -0,0 +1,90 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
/* eslint-disable max-classes-per-file */
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { hasValue } from '../../shared/empty.util';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { RemoteData } from './remote-data';
import { IdentifierType } from './request.models';
import { RequestService } from './request.service';
import { getFirstCompletedRemoteData } from '../shared/operators';
import { DSpaceObject } from '../shared/dspace-object.model';
import { IdentifiableDataService } from './base/identifiable-data.service';
import { getDSORoute } from '../../app-routing-paths';
const ID_ENDPOINT = 'pid';
const UUID_ENDPOINT = 'dso';
class DsoByIdOrUUIDService extends IdentifiableDataService<DSpaceObject> {
constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
) {
super(
undefined, requestService, rdbService, objectCache, halService, undefined,
// interpolate id/uuid as query parameter
(endpoint: string, resourceID: string): string => {
return endpoint.replace(/{\?id}/, `?id=${resourceID}`)
.replace(/{\?uuid}/, `?uuid=${resourceID}`);
},
);
}
/**
* The default 'pid' endpoint for identifiers does not support uuid lookups.
* For uuid lookups we need to change the linkPath.
* @param identifierType
*/
setLinkPath(identifierType: IdentifierType) {
if (identifierType === IdentifierType.UUID) {
this.linkPath = UUID_ENDPOINT;
} else {
this.linkPath = ID_ENDPOINT;
}
}
}
@Injectable()
export class DsoRedirectService {
private dataService: DsoByIdOrUUIDService;
constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
private router: Router,
) {
this.dataService = new DsoByIdOrUUIDService(requestService, rdbService, objectCache, halService);
}
findByIdAndIDType(id: string, identifierType = IdentifierType.UUID): Observable<RemoteData<DSpaceObject>> {
this.dataService.setLinkPath(identifierType);
return this.dataService.findById(id).pipe(
getFirstCompletedRemoteData(),
tap((response) => {
if (response.hasSucceeded) {
const dso = response.payload;
if (hasValue(dso.uuid)) {
let newRoute = getDSORoute(dso);
if (hasValue(newRoute)) {
this.router.navigate([newRoute]);
}
}
}
})
);
}
}

View File

@@ -7,8 +7,6 @@ import { GetRequest } from './request.models';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { DSpaceObjectDataService } from './dspace-object-data.service'; import { DSpaceObjectDataService } from './dspace-object-data.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
describe('DSpaceObjectDataService', () => { describe('DSpaceObjectDataService', () => {
let scheduler: TestScheduler; let scheduler: TestScheduler;
@@ -42,18 +40,12 @@ describe('DSpaceObjectDataService', () => {
}) })
}); });
objectCache = {} as ObjectCacheService; objectCache = {} as ObjectCacheService;
const notificationsService = {} as NotificationsService;
const http = {} as HttpClient;
const comparator = {} as any;
service = new DSpaceObjectDataService( service = new DSpaceObjectDataService(
requestService, requestService,
rdbService, rdbService,
objectCache, objectCache,
halService, halService,
notificationsService,
http,
comparator
); );
}); });

View File

@@ -1,106 +1,28 @@
/* eslint-disable max-classes-per-file */
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { DSpaceObject } from '../shared/dspace-object.model'; import { DSpaceObject } from '../shared/dspace-object.model';
import { DSPACE_OBJECT } from '../shared/dspace-object.resource-type'; import { DSPACE_OBJECT } from '../shared/dspace-object.resource-type';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { DataService } from './data.service';
import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
import { RemoteData } from './remote-data';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { PaginatedList } from './paginated-list.model'; import { IdentifiableDataService } from './base/identifiable-data.service';
import { CoreState } from '../core-state.model';
import { FindListOptions } from './find-list-options.model';
class DataServiceImpl extends DataService<DSpaceObject> {
protected linkPath = 'dso';
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: DSOChangeAnalyzer<DSpaceObject>) {
super();
}
getIDHref(endpoint, resourceID, ...linksToFollow: FollowLinkConfig<DSpaceObject>[]): string {
return this.buildHrefFromFindOptions( endpoint.replace(/\{\?uuid\}/, `?uuid=${resourceID}`),
{}, [], ...linksToFollow);
}
}
@Injectable() @Injectable()
@dataService(DSPACE_OBJECT) @dataService(DSPACE_OBJECT)
export class DSpaceObjectDataService { export class DSpaceObjectDataService extends IdentifiableDataService<DSpaceObject> {
protected linkPath = 'dso';
private dataService: DataServiceImpl;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, ) {
protected http: HttpClient, super(
protected comparator: DSOChangeAnalyzer<DSpaceObject>) { 'dso', requestService, rdbService, objectCache, halService, undefined,
this.dataService = new DataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparator); // interpolate uuid as query parameter
(endpoint: string, resourceID: string): string => {
return endpoint.replace(/{\?uuid}/, `?uuid=${resourceID}`);
},
);
} }
/**
* Returns an observable of {@link RemoteData} of an object, based on its ID, with a list of {@link FollowLinkConfig},
* to automatically resolve {@link HALLink}s of the object
* @param id ID of object we want to retrieve
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
findById(id: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<DSpaceObject>[]): Observable<RemoteData<DSpaceObject>> {
return this.dataService.findById(id, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Returns an observable of {@link RemoteData} of an object, based on an href, with a list of {@link FollowLinkConfig},
* to automatically resolve {@link HALLink}s of the object
* @param href The url of object we want to retrieve
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
findByHref(href: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<DSpaceObject>[]): Observable<RemoteData<DSpaceObject>> {
return this.dataService.findByHref(href, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Returns a list of observables of {@link RemoteData} of objects, based on an href, with a list of {@link FollowLinkConfig},
* to automatically resolve {@link HALLink}s of the object
* @param href The url of object we want to retrieve
* @param findListOptions Find list options object
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
findAllByHref(href: string, findListOptions: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<DSpaceObject>[]): Observable<RemoteData<PaginatedList<DSpaceObject>>> {
return this.dataService.findAllByHref(href, findListOptions, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
} }

View File

@@ -1,13 +1,8 @@
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { DataService } from './data.service';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { filter, map, switchMap, take } from 'rxjs/operators'; import { filter, map, switchMap, take } from 'rxjs/operators';
@@ -17,27 +12,30 @@ import { PaginatedList } from './paginated-list.model';
import { ItemType } from '../shared/item-relationships/item-type.model'; import { ItemType } from '../shared/item-relationships/item-type.model';
import { getFirstSucceededRemoteData, getRemoteDataPayload } from '../shared/operators'; import { getFirstSucceededRemoteData, getRemoteDataPayload } from '../shared/operators';
import { RelationshipTypeService } from './relationship-type.service'; import { RelationshipTypeService } from './relationship-type.service';
import { CoreState } from '../core-state.model';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
import { BaseDataService } from './base/base-data.service';
import { SearchData, SearchDataImpl } from './base/search-data';
import { FindAllData, FindAllDataImpl } from './base/find-all-data';
/** /**
* Service handling all ItemType requests * Service handling all ItemType requests
*/ */
@Injectable() @Injectable()
export class EntityTypeService extends DataService<ItemType> { export class EntityTypeService extends BaseDataService<ItemType> implements FindAllData<ItemType>, SearchData<ItemType> {
private findAllData: FindAllData<ItemType>;
private searchData: SearchDataImpl<ItemType>;
protected linkPath = 'entitytypes'; constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected relationshipTypeService: RelationshipTypeService,
) {
super('entitytypes', requestService, rdbService, objectCache, halService);
constructor(protected requestService: RequestService, this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
protected rdbService: RemoteDataBuildService, this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
protected store: Store<CoreState>,
protected halService: HALEndpointService,
protected objectCache: ObjectCacheService,
protected notificationsService: NotificationsService,
protected relationshipTypeService: RelationshipTypeService,
protected http: HttpClient,
protected comparator: DefaultChangeAnalyzer<ItemType>) {
super();
} }
getBrowseEndpoint(options, linkPath?: string): Observable<string> { getBrowseEndpoint(options, linkPath?: string): Observable<string> {
@@ -158,7 +156,43 @@ export class EntityTypeService extends DataService<ItemType> {
return this.halService.getEndpoint(this.linkPath).pipe( return this.halService.getEndpoint(this.linkPath).pipe(
take(1), take(1),
switchMap((endPoint: string) => switchMap((endPoint: string) =>
this.findByHref(endPoint + '/label/' + label)) this.findByHref(endPoint + '/label/' + label)),
); );
} }
/**
* 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 useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>>}
* Return an observable that emits object list
*/
public findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<ItemType>[]): Observable<RemoteData<PaginatedList<ItemType>>> {
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Make a new FindListRequest with given search method
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>}
* Return an observable that emits response from the server
*/
searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<ItemType>[]): Observable<RemoteData<PaginatedList<ItemType>>> {
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
} }

View File

@@ -13,11 +13,9 @@ import { RegistrationResponseParsingService } from './registration-response-pars
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
@Injectable( @Injectable({
{ providedIn: 'root',
providedIn: 'root', })
}
)
/** /**
* Service that will register a new email address and request a token * Service that will register a new email address and request a token
*/ */

View File

@@ -48,9 +48,9 @@ describe('ExternalSourceService', () => {
buildList: createSuccessfulRemoteDataObject$(createPaginatedList(entries)) buildList: createSuccessfulRemoteDataObject$(createPaginatedList(entries))
}); });
halService = jasmine.createSpyObj('halService', { halService = jasmine.createSpyObj('halService', {
getEndpoint: observableOf('external-sources-REST-endpoint') getEndpoint: observableOf('external-sources-REST-endpoint'),
}); });
service = new ExternalSourceService(requestService, rdbService, undefined, undefined, halService, undefined, undefined, undefined); service = new ExternalSourceService(requestService, rdbService, undefined, halService);
} }
beforeEach(() => { beforeEach(() => {

View File

@@ -1,13 +1,9 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { DataService } from './data.service';
import { ExternalSource } from '../shared/external-source.model'; import { ExternalSource } from '../shared/external-source.model';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { distinctUntilChanged, map, switchMap, take } from 'rxjs/operators'; import { distinctUntilChanged, map, switchMap, take } from 'rxjs/operators';
import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model';
@@ -15,28 +11,27 @@ import { hasValue, isNotEmptyOperator } from '../../shared/empty.util';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { PaginatedList } from './paginated-list.model'; import { PaginatedList } from './paginated-list.model';
import { ExternalSourceEntry } from '../shared/external-source-entry.model'; import { ExternalSourceEntry } from '../shared/external-source-entry.model';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { CoreState } from '../core-state.model';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
import { IdentifiableDataService } from './base/identifiable-data.service';
import { SearchData, SearchDataImpl } from './base/search-data';
/** /**
* A service handling all external source requests * A service handling all external source requests
*/ */
@Injectable() @Injectable()
export class ExternalSourceService extends DataService<ExternalSource> { export class ExternalSourceService extends IdentifiableDataService<ExternalSource> implements SearchData<ExternalSource> {
protected linkPath = 'externalsources'; private searchData: SearchData<ExternalSource>;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, ) {
protected http: HttpClient, super('externalsources', requestService, rdbService, objectCache, halService);
protected comparator: DefaultChangeAnalyzer<ExternalSource>) {
super(); this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
} }
/** /**
@@ -75,10 +70,28 @@ export class ExternalSourceService extends DataService<ExternalSource> {
isNotEmptyOperator(), isNotEmptyOperator(),
distinctUntilChanged(), distinctUntilChanged(),
map((endpoint: string) => hasValue(searchOptions) ? searchOptions.toRestUrl(endpoint) : endpoint), map((endpoint: string) => hasValue(searchOptions) ? searchOptions.toRestUrl(endpoint) : endpoint),
take(1) take(1),
); );
// TODO create a dedicated ExternalSourceEntryDataService and move this entire method to it. Then the "as any"s won't be necessary // TODO create a dedicated ExternalSourceEntryDataService and move this entire method to it. Then the "as any"s won't be necessary
return this.findAllByHref(href$, undefined, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow as any) as any; return this.findAllByHref(href$, undefined, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow as any) as any;
} }
/**
* Make a new FindListRequest with given search method
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>}
* Return an observable that emits response from the server
*/
public searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<ExternalSource>[]): Observable<RemoteData<PaginatedList<ExternalSource>>> {
return undefined;
}
} }

View File

@@ -1,6 +1,5 @@
import { AuthorizationDataService } from './authorization-data.service'; import { AuthorizationDataService } from './authorization-data.service';
import { SiteDataService } from '../site-data.service'; import { SiteDataService } from '../site-data.service';
import { AuthService } from '../../auth/auth.service';
import { Site } from '../../shared/site.model'; import { Site } from '../../shared/site.model';
import { EPerson } from '../../eperson/models/eperson.model'; import { EPerson } from '../../eperson/models/eperson.model';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
@@ -16,7 +15,6 @@ import { FindListOptions } from '../find-list-options.model';
describe('AuthorizationDataService', () => { describe('AuthorizationDataService', () => {
let service: AuthorizationDataService; let service: AuthorizationDataService;
let siteService: SiteDataService; let siteService: SiteDataService;
let authService: AuthService;
let site: Site; let site: Site;
let ePerson: EPerson; let ePerson: EPerson;
@@ -37,13 +35,9 @@ describe('AuthorizationDataService', () => {
uuid: 'test-eperson' uuid: 'test-eperson'
}); });
siteService = jasmine.createSpyObj('siteService', { siteService = jasmine.createSpyObj('siteService', {
find: observableOf(site) find: observableOf(site),
}); });
authService = { service = new AuthorizationDataService(requestService, undefined, undefined, undefined, siteService);
isAuthenticated: () => observableOf(true),
getAuthenticatedUserFromStore: () => observableOf(ePerson)
} as AuthService;
service = new AuthorizationDataService(requestService, undefined, undefined, undefined, undefined, undefined, undefined, undefined, authService, siteService);
} }
beforeEach(() => { beforeEach(() => {

View File

@@ -2,17 +2,11 @@ import { Observable, of as observableOf } from 'rxjs';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { AUTHORIZATION } from '../../shared/authorization.resource-type'; import { AUTHORIZATION } from '../../shared/authorization.resource-type';
import { dataService } from '../../cache/builders/build-decorators'; import { dataService } from '../../cache/builders/build-decorators';
import { DataService } from '../data.service';
import { Authorization } from '../../shared/authorization.model'; import { Authorization } from '../../shared/authorization.model';
import { RequestService } from '../request.service'; import { RequestService } from '../request.service';
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../../cache/object-cache.service'; import { ObjectCacheService } from '../../cache/object-cache.service';
import { HALEndpointService } from '../../shared/hal-endpoint.service'; import { HALEndpointService } from '../../shared/hal-endpoint.service';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DSOChangeAnalyzer } from '../dso-change-analyzer.service';
import { AuthService } from '../../auth/auth.service';
import { SiteDataService } from '../site-data.service'; import { SiteDataService } from '../site-data.service';
import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
import { RemoteData } from '../remote-data'; import { RemoteData } from '../remote-data';
@@ -24,31 +18,31 @@ import { AuthorizationSearchParams } from './authorization-search-params';
import { addSiteObjectUrlIfEmpty, oneAuthorizationMatchesFeature } from './authorization-utils'; import { addSiteObjectUrlIfEmpty, oneAuthorizationMatchesFeature } from './authorization-utils';
import { FeatureID } from './feature-id'; import { FeatureID } from './feature-id';
import { getFirstCompletedRemoteData } from '../../shared/operators'; import { getFirstCompletedRemoteData } from '../../shared/operators';
import { CoreState } from '../../core-state.model';
import { FindListOptions } from '../find-list-options.model'; import { FindListOptions } from '../find-list-options.model';
import { BaseDataService } from '../base/base-data.service';
import { SearchData, SearchDataImpl } from '../base/search-data';
/** /**
* A service to retrieve {@link Authorization}s from the REST API * A service to retrieve {@link Authorization}s from the REST API
*/ */
@Injectable() @Injectable()
@dataService(AUTHORIZATION) @dataService(AUTHORIZATION)
export class AuthorizationDataService extends DataService<Authorization> { export class AuthorizationDataService extends BaseDataService<Authorization> implements SearchData<Authorization> {
protected linkPath = 'authorizations'; protected linkPath = 'authorizations';
protected searchByObjectPath = 'object'; protected searchByObjectPath = 'object';
private searchData: SearchDataImpl<Authorization>;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, protected siteService: SiteDataService,
protected http: HttpClient,
protected comparator: DSOChangeAnalyzer<Authorization>,
protected authService: AuthService,
protected siteService: SiteDataService
) { ) {
super(); super('authorizations', requestService, rdbService, objectCache, halService);
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
} }
/** /**
@@ -130,7 +124,25 @@ export class AuthorizationDataService extends DataService<Authorization> {
params.push(new RequestParam('eperson', ePersonUuid)); params.push(new RequestParam('eperson', ePersonUuid));
} }
return Object.assign(new FindListOptions(), options, { return Object.assign(new FindListOptions(), options, {
searchParams: [...params] searchParams: [...params],
}); });
} }
/**
* Make a new FindListRequest with given search method
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>}
* Return an observable that emits response from the server
*/
searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<Authorization>[]): Observable<RemoteData<PaginatedList<Authorization>>> {
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
} }

View File

@@ -1,36 +1,27 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { FEATURE } from '../../shared/feature.resource-type'; import { FEATURE } from '../../shared/feature.resource-type';
import { dataService } from '../../cache/builders/build-decorators'; import { dataService } from '../../cache/builders/build-decorators';
import { DataService } from '../data.service';
import { Feature } from '../../shared/feature.model'; import { Feature } from '../../shared/feature.model';
import { RequestService } from '../request.service'; import { RequestService } from '../request.service';
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../../cache/object-cache.service'; import { ObjectCacheService } from '../../cache/object-cache.service';
import { HALEndpointService } from '../../shared/hal-endpoint.service'; import { HALEndpointService } from '../../shared/hal-endpoint.service';
import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { BaseDataService } from '../base/base-data.service';
import { HttpClient } from '@angular/common/http';
import { DSOChangeAnalyzer } from '../dso-change-analyzer.service';
import { CoreState } from '../../core-state.model';
/** /**
* A service to retrieve {@link Feature}s from the REST API * A service to retrieve {@link Feature}s from the REST API
*/ */
@Injectable() @Injectable()
@dataService(FEATURE) @dataService(FEATURE)
export class FeatureDataService extends DataService<Feature> { export class FeatureDataService extends BaseDataService<Feature> {
protected linkPath = 'features'; protected linkPath = 'features';
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DSOChangeAnalyzer<Feature>
) { ) {
super(); super('features', requestService, rdbService, objectCache, halService);
} }
} }

View File

@@ -1,8 +1,8 @@
import { HrefOnlyDataService } from './href-only-data.service'; import { HrefOnlyDataService } from './href-only-data.service';
import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
import { DataService } from './data.service';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
import { BaseDataService } from './base/base-data.service';
describe(`HrefOnlyDataService`, () => { describe(`HrefOnlyDataService`, () => {
let service: HrefOnlyDataService; let service: HrefOnlyDataService;
@@ -15,12 +15,12 @@ describe(`HrefOnlyDataService`, () => {
href = 'https://rest.api/server/api/core/items/de7fa215-4a25-43a7-a4d7-17534a09fdfc'; href = 'https://rest.api/server/api/core/items/de7fa215-4a25-43a7-a4d7-17534a09fdfc';
followLinks = [ followLink('link1'), followLink('link2') ]; followLinks = [ followLink('link1'), followLink('link2') ];
findListOptions = new FindListOptions(); findListOptions = new FindListOptions();
service = new HrefOnlyDataService(null, null, null, null, null, null, null, null); service = new HrefOnlyDataService(null, null, null, null);
}); });
it(`should instantiate a private DataService`, () => { it(`should instantiate a private DataService`, () => {
expect((service as any).dataService).toBeDefined(); expect((service as any).dataService).toBeDefined();
expect((service as any).dataService).toBeInstanceOf(DataService); expect((service as any).dataService).toBeInstanceOf(BaseDataService);
}); });
describe(`findByHref`, () => { describe(`findByHref`, () => {
@@ -28,7 +28,7 @@ describe(`HrefOnlyDataService`, () => {
spy = spyOn((service as any).dataService, 'findByHref').and.returnValue(createSuccessfulRemoteDataObject$(null)); spy = spyOn((service as any).dataService, 'findByHref').and.returnValue(createSuccessfulRemoteDataObject$(null));
}); });
it(`should delegate to findByHref on the internal DataService`, () => { it(`should forward to findByHref on the internal DataService`, () => {
service.findByHref(href, false, false, ...followLinks); service.findByHref(href, false, false, ...followLinks);
expect(spy).toHaveBeenCalledWith(href, false, false, ...followLinks); expect(spy).toHaveBeenCalledWith(href, false, false, ...followLinks);
}); });

View File

@@ -1,13 +1,7 @@
/* eslint-disable max-classes-per-file */
import { DataService } from './data.service';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { VOCABULARY_ENTRY } from '../submission/vocabularies/models/vocabularies.resource-type'; import { VOCABULARY_ENTRY } from '../submission/vocabularies/models/vocabularies.resource-type';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
@@ -18,25 +12,8 @@ import { PaginatedList } from './paginated-list.model';
import { ITEM_TYPE } from '../shared/item-relationships/item-type.resource-type'; import { ITEM_TYPE } from '../shared/item-relationships/item-type.resource-type';
import { LICENSE } from '../shared/license.resource-type'; import { LICENSE } from '../shared/license.resource-type';
import { CacheableObject } from '../cache/cacheable-object.model'; import { CacheableObject } from '../cache/cacheable-object.model';
import { CoreState } from '../core-state.model';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
import { BaseDataService } from './base/base-data.service';
class DataServiceImpl extends DataService<any> {
// linkPath isn't used if we're only searching by href.
protected linkPath = undefined;
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: DefaultChangeAnalyzer<any>) {
super();
}
}
/** /**
* A DataService with only findByHref methods. Its purpose is to be used for resources that don't * A DataService with only findByHref methods. Its purpose is to be used for resources that don't
@@ -46,24 +23,25 @@ class DataServiceImpl extends DataService<any> {
* an @dataService annotation can be added for any number of these resource types * an @dataService annotation can be added for any number of these resource types
*/ */
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root',
}) })
@dataService(VOCABULARY_ENTRY) @dataService(VOCABULARY_ENTRY)
@dataService(ITEM_TYPE) @dataService(ITEM_TYPE)
@dataService(LICENSE) @dataService(LICENSE)
export class HrefOnlyDataService { export class HrefOnlyDataService {
private dataService: DataServiceImpl; /**
* Not all BaseDataService methods should be exposed, so
* @private
*/
private dataService: BaseDataService<any>;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, ) {
protected http: HttpClient, this.dataService = new BaseDataService(undefined, requestService, rdbService, objectCache, halService);
protected comparator: DefaultChangeAnalyzer<any>) {
this.dataService = new DataServiceImpl(requestService, rdbService, store, objectCache, halService, notificationsService, http, comparator);
} }
/** /**

View File

@@ -21,7 +21,7 @@ import { HALEndpointServiceStub } from 'src/app/shared/testing/hal-endpoint-serv
describe('ItemDataService', () => { describe('ItemDataService', () => {
let scheduler: TestScheduler; let scheduler: TestScheduler;
let service: ItemDataService; let service: ItemDataService;
let bs: BrowseService; let browseService: BrowseService;
const requestService = Object.assign(getMockRequestService(), { const requestService = Object.assign(getMockRequestService(), {
generateRequestId(): string { generateRequestId(): string {
return scopeID; return scopeID;
@@ -78,14 +78,12 @@ describe('ItemDataService', () => {
return new ItemDataService( return new ItemDataService(
requestService, requestService,
rdbService, rdbService,
store,
bs,
objectCache, objectCache,
halEndpointService, halEndpointService,
notificationsService, notificationsService,
http,
comparator, comparator,
bundleService browseService,
bundleService,
); );
} }
@@ -95,7 +93,7 @@ describe('ItemDataService', () => {
}); });
it('should return the endpoint to fetch Items within the given scope and starting with the given string', () => { it('should return the endpoint to fetch Items within the given scope and starting with the given string', () => {
bs = initMockBrowseService(true); browseService = initMockBrowseService(true);
service = initTestService(); service = initTestService();
const result = service.getBrowseEndpoint(options); const result = service.getBrowseEndpoint(options);
@@ -106,7 +104,7 @@ describe('ItemDataService', () => {
describe('if the dc.date.issue browse isn\'t configured for items', () => { describe('if the dc.date.issue browse isn\'t configured for items', () => {
beforeEach(() => { beforeEach(() => {
bs = initMockBrowseService(false); browseService = initMockBrowseService(false);
service = initTestService(); service = initTestService();
}); });
it('should throw an error', () => { it('should throw an error', () => {

View File

@@ -1,6 +1,13 @@
import { HttpClient, HttpHeaders } from '@angular/common/http'; /**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
/* eslint-disable max-classes-per-file */
import { HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { distinctUntilChanged, filter, find, map, switchMap, take } from 'rxjs/operators'; import { distinctUntilChanged, filter, find, map, switchMap, take } from 'rxjs/operators';
import { hasValue, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util'; import { hasValue, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
@@ -16,12 +23,10 @@ import { HALEndpointService } from '../shared/hal-endpoint.service';
import { Item } from '../shared/item.model'; import { Item } from '../shared/item.model';
import { ITEM } from '../shared/item.resource-type'; import { ITEM } from '../shared/item.resource-type';
import { URLCombiner } from '../url-combiner/url-combiner'; import { URLCombiner } from '../url-combiner/url-combiner';
import { DataService } from './data.service';
import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
import { PaginatedList } from './paginated-list.model'; import { PaginatedList } from './paginated-list.model';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { DeleteRequest, GetRequest, PostRequest, PutRequest} from './request.models'; import { DeleteRequest, GetRequest, PostRequest, PutRequest } from './request.models';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model';
import { Bundle } from '../shared/bundle.model'; import { Bundle } from '../shared/bundle.model';
@@ -34,27 +39,41 @@ import { ResponseParsingService } from './parsing.service';
import { StatusCodeOnlyResponseParsingService } from './status-code-only-response-parsing.service'; import { StatusCodeOnlyResponseParsingService } from './status-code-only-response-parsing.service';
import { sendRequest } from '../shared/request.operators'; import { sendRequest } from '../shared/request.operators';
import { RestRequest } from './rest-request.model'; import { RestRequest } from './rest-request.model';
import { CoreState } from '../core-state.model';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
import { ConstructIdEndpoint, IdentifiableDataService } from './base/identifiable-data.service';
import { PatchData, PatchDataImpl } from './base/patch-data';
import { DeleteData, DeleteDataImpl } from './base/delete-data';
import { RestRequestMethod } from './rest-request-method';
import { CreateData, CreateDataImpl } from './base/create-data';
import { RequestParam } from '../cache/models/request-param.model';
@Injectable() /**
@dataService(ITEM) * An abstract service for CRUD operations on Items
export class ItemDataService extends DataService<Item> { * Doesn't specify an endpoint because multiple endpoints support Item-like functionality (e.g. items, itemtemplates)
protected linkPath = 'items'; * Extend this class to implement data services for Items
*/
export abstract class BaseItemDataService extends IdentifiableDataService<Item> implements CreateData<Item>, PatchData<Item>, DeleteData<Item> {
private createData: CreateData<Item>;
private patchData: PatchData<Item>;
private deleteData: DeleteData<Item>;
constructor( protected constructor(
protected linkPath,
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected bs: BrowseService,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DSOChangeAnalyzer<Item>, protected comparator: DSOChangeAnalyzer<Item>,
protected bundleService: BundleDataService protected browseService: BrowseService,
protected bundleService: BundleDataService,
protected constructIdEndpoint: ConstructIdEndpoint = (endpoint, resourceID) => `${endpoint}/${resourceID}`,
) { ) {
super(); super(linkPath, requestService, rdbService, objectCache, halService, undefined, constructIdEndpoint);
this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive);
this.patchData = new PatchDataImpl<Item>(this.linkPath, requestService, rdbService, objectCache, halService, comparator, this.responseMsToLive, this.constructIdEndpoint);
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
} }
/** /**
@@ -69,10 +88,11 @@ export class ItemDataService extends DataService<Item> {
if (options.sort && options.sort.field) { if (options.sort && options.sort.field) {
field = options.sort.field; field = options.sort.field;
} }
return this.bs.getBrowseURLFor(field, linkPath).pipe( return this.browseService.getBrowseURLFor(field, linkPath).pipe(
filter((href: string) => isNotEmpty(href)), filter((href: string) => isNotEmpty(href)),
map((href: string) => new URLCombiner(href, `?scope=${options.scopeID}`).toString()), map((href: string) => new URLCombiner(href, `?scope=${options.scopeID}`).toString()),
distinctUntilChanged(),); distinctUntilChanged(),
);
} }
/** /**
@@ -84,7 +104,7 @@ export class ItemDataService extends DataService<Item> {
public getMappedCollectionsEndpoint(itemId: string, collectionId?: string): Observable<string> { public getMappedCollectionsEndpoint(itemId: string, collectionId?: string): Observable<string> {
return this.halService.getEndpoint(this.linkPath).pipe( return this.halService.getEndpoint(this.linkPath).pipe(
map((endpoint: string) => this.getIDHref(endpoint, itemId)), map((endpoint: string) => this.getIDHref(endpoint, itemId)),
map((endpoint: string) => `${endpoint}/mappedCollections${collectionId ? `/${collectionId}` : ''}`) map((endpoint: string) => `${endpoint}/mappedCollections${collectionId ? `/${collectionId}` : ''}`),
); );
} }
@@ -219,7 +239,7 @@ export class ItemDataService extends DataService<Item> {
public getMoveItemEndpoint(itemId: string): Observable<string> { public getMoveItemEndpoint(itemId: string): Observable<string> {
return this.halService.getEndpoint(this.linkPath).pipe( return this.halService.getEndpoint(this.linkPath).pipe(
map((endpoint: string) => this.getIDHref(endpoint, itemId)), map((endpoint: string) => this.getIDHref(endpoint, itemId)),
map((endpoint: string) => `${endpoint}/owningCollection`) map((endpoint: string) => `${endpoint}/owningCollection`),
); );
} }
@@ -299,4 +319,85 @@ export class ItemDataService extends DataService<Item> {
this.requestService.setStaleByHrefSubstring('item/' + itemUUID); this.requestService.setStaleByHrefSubstring('item/' + itemUUID);
} }
/**
* Commit current object changes to the server
* @param method The RestRequestMethod for which de server sync buffer should be committed
*/
public commitUpdates(method?: RestRequestMethod): void {
this.patchData.commitUpdates(method);
}
/**
* Send a patch request for a specified object
* @param {T} object The object to send a patch request for
* @param {Operation[]} operations The patch operations to be performed
*/
public patch(object: Item, operations: Operation[]): Observable<RemoteData<Item>> {
return this.patchData.patch(object, operations);
}
/**
* Add a new patch to the object cache
* The patch is derived from the differences between the given object and its version in the object cache
* @param {DSpaceObject} object The given object
*/
public update(object: Item): Observable<RemoteData<Item>> {
return this.patchData.update(object);
}
/**
* Delete an existing object on the server
* @param objectId The id of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
*/
public delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.delete(objectId, copyVirtualMetadata);
}
/**
* Delete an existing object on the server
* @param href The self link of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
* Only emits once all request related to the DSO has been invalidated.
*/
public deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
}
/**
* Create a new object on the server, and store the response in the object cache
*
* @param object The object to create
* @param params Array with additional params to combine with query string
*/
public create(object: Item, ...params: RequestParam[]): Observable<RemoteData<Item>> {
return this.createData.create(object, ...params);
}
}
/**
* A service for CRUD operations on Items
*/
@Injectable()
@dataService(ITEM)
export class ItemDataService extends BaseItemDataService {
constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected comparator: DSOChangeAnalyzer<Item>,
protected browseService: BrowseService,
protected bundleService: BundleDataService,
) {
super('items', requestService, rdbService, objectCache, halService, notificationsService, comparator, browseService, bundleService);
}
} }

View File

@@ -35,7 +35,7 @@ describe('ItemRequestDataService', () => {
getEndpoint: observableOf(restApiEndpoint), getEndpoint: observableOf(restApiEndpoint),
}); });
service = new ItemRequestDataService(requestService, rdbService, null, null, halService, null, null, null); service = new ItemRequestDataService(requestService, rdbService, null, halService);
}); });
describe('requestACopy', () => { describe('requestACopy', () => {

View File

@@ -9,40 +9,27 @@ import { PostRequest, PutRequest } from './request.models';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { ItemRequest } from '../shared/item-request.model'; import { ItemRequest } from '../shared/item-request.model';
import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { hasValue, isNotEmpty } from '../../shared/empty.util';
import { DataService } from './data.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpHeaders } from '@angular/common/http';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { RequestCopyEmail } from '../../request-copy/email-request-copy/request-copy-email.model'; import { RequestCopyEmail } from '../../request-copy/email-request-copy/request-copy-email.model';
import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { HttpOptions } from '../dspace-rest/dspace-rest.service';
import { CoreState } from '../core-state.model';
import { sendRequest } from '../shared/request.operators'; import { sendRequest } from '../shared/request.operators';
import { IdentifiableDataService } from './base/identifiable-data.service';
/** /**
* A service responsible for fetching/sending data from/to the REST API on the itemrequests endpoint * A service responsible for fetching/sending data from/to the REST API on the itemrequests endpoint
*/ */
@Injectable( @Injectable({
{ providedIn: 'root',
providedIn: 'root', })
} export class ItemRequestDataService extends IdentifiableDataService<ItemRequest> {
)
export class ItemRequestDataService extends DataService<ItemRequest> {
protected linkPath = 'itemrequests';
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DefaultChangeAnalyzer<ItemRequest>,
) { ) {
super(); super('itemrequests', requestService, rdbService, objectCache, halService);
} }
getItemRequestEndpoint(): Observable<string> { getItemRequestEndpoint(): Observable<string> {
@@ -124,9 +111,9 @@ export class ItemRequestDataService extends DataService<ItemRequest> {
suggestOpenAccess, suggestOpenAccess,
}), options); }), options);
}), }),
sendRequest(this.requestService)).subscribe(); sendRequest(this.requestService),
).subscribe();
return this.rdbService.buildFromRequestUUID(requestId); return this.rdbService.buildFromRequestUUID(requestId);
} }
} }

View File

@@ -8,17 +8,17 @@ import { BrowseService } from '../browse/browse.service';
import { cold } from 'jasmine-marbles'; import { cold } from 'jasmine-marbles';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { CollectionDataService } from './collection-data.service'; import { CollectionDataService } from './collection-data.service';
import { RestRequestMethod } from './rest-request-method'; import { RestRequestMethod } from './rest-request-method';
import { Item } from '../shared/item.model'; import { Item } from '../shared/item.model';
import { RestRequest } from './rest-request.model'; import { RestRequest } from './rest-request.model';
import { CoreState } from '../core-state.model'; import { CoreState } from '../core-state.model';
import { RequestEntry } from './request-entry.model'; import { RequestEntry } from './request-entry.model';
import createSpyObj = jasmine.createSpyObj;
describe('ItemTemplateDataService', () => { describe('ItemTemplateDataService', () => {
let service: ItemTemplateDataService; let service: ItemTemplateDataService;
let itemService: any; let byCollection: any;
const item = new Item(); const item = new Item();
const collectionEndpoint = 'https://rest.api/core/collections/4af28e99-6a9c-4036-a199-e1b587046d39'; const collectionEndpoint = 'https://rest.api/core/collections/4af28e99-6a9c-4036-a199-e1b587046d39';
@@ -47,14 +47,14 @@ describe('ItemTemplateDataService', () => {
} as RequestService; } as RequestService;
const rdbService = {} as RemoteDataBuildService; const rdbService = {} as RemoteDataBuildService;
const store = {} as Store<CoreState>; const store = {} as Store<CoreState>;
const bs = {} as BrowseService; const browseService = {} as BrowseService;
const objectCache = { const objectCache = {
getObjectBySelfLink(self) { getObjectBySelfLink(self) {
return observableOf({}); return observableOf({});
}, },
addPatch(self, operations) { addPatch(self, operations) {
// Do nothing // Do nothing
} },
} as any; } as any;
const halEndpointService = { const halEndpointService = {
getEndpoint(linkPath: string): Observable<string> { getEndpoint(linkPath: string): Observable<string> {
@@ -62,7 +62,6 @@ describe('ItemTemplateDataService', () => {
} }
} as HALEndpointService; } as HALEndpointService;
const notificationsService = {} as NotificationsService; const notificationsService = {} as NotificationsService;
const http = {} as HttpClient;
const comparator = { const comparator = {
diff(first, second) { diff(first, second) {
return [{}]; return [{}];
@@ -78,60 +77,61 @@ describe('ItemTemplateDataService', () => {
service = new ItemTemplateDataService( service = new ItemTemplateDataService(
requestService, requestService,
rdbService, rdbService,
store,
bs,
objectCache, objectCache,
halEndpointService, halEndpointService,
notificationsService, notificationsService,
http,
comparator, comparator,
browseService,
undefined, undefined,
collectionService collectionService,
); );
itemService = (service as any).dataService; byCollection = (service as any).byCollection;
} }
beforeEach(() => { beforeEach(() => {
initTestService(); initTestService();
}); });
describe('commitUpdates', () => {
it('should call commitUpdates on the item service implementation', () => {
spyOn(itemService, 'commitUpdates');
service.commitUpdates();
expect(itemService.commitUpdates).toHaveBeenCalled();
});
});
describe('update', () => {
it('should call update on the item service implementation', () => {
spyOn(itemService, 'update');
service.update(item);
expect(itemService.update).toHaveBeenCalled();
});
});
describe('findByCollectionID', () => { describe('findByCollectionID', () => {
it('should call findByCollectionID on the item service implementation', () => { it('should call findByCollectionID on the collection-based data service', () => {
spyOn(itemService, 'findByCollectionID'); spyOn(byCollection, 'findById');
service.findByCollectionID(scopeID); service.findByCollectionID(scopeID);
expect(itemService.findByCollectionID).toHaveBeenCalled(); expect(byCollection.findById).toHaveBeenCalled();
}); });
}); });
describe('create', () => { describe('createByCollectionID', () => {
it('should call createTemplate on the item service implementation', () => { it('should call createTemplate on the collection-based data service', () => {
spyOn(itemService, 'createTemplate'); spyOn(byCollection, 'createTemplate');
service.create(item, scopeID); service.createByCollectionID(item, scopeID);
expect(itemService.createTemplate).toHaveBeenCalled(); expect(byCollection.createTemplate).toHaveBeenCalledWith(item, scopeID);
}); });
}); });
describe('deleteByCollectionID', () => { describe('byCollection', () => {
it('should call deleteByCollectionID on the item service implementation', () => { beforeEach(() => {
spyOn(itemService, 'deleteByCollectionID'); byCollection.createData = createSpyObj('createData', {
service.deleteByCollectionID(item, scopeID); createOnEndpoint: 'TEST createOnEndpoint',
expect(itemService.deleteByCollectionID).toHaveBeenCalled(); });
});
describe('getIDHrefObs', () => {
it('should point to the Item template of a given Collection', () => {
expect(byCollection.getIDHrefObs(scopeID)).toBeObservable(cold('a', { a: jasmine.stringMatching(`/collections/${scopeID}/itemtemplate`) }));
});
});
describe('createTemplate', () => {
it('should forward to CreateDataImpl.createOnEndpoint', () => {
spyOn(byCollection, 'getIDHrefObs').and.returnValue('TEST getIDHrefObs');
const out = byCollection.createTemplate(item, scopeID);
expect(byCollection.getIDHrefObs).toHaveBeenCalledWith(scopeID);
expect(byCollection.createData.createOnEndpoint).toHaveBeenCalledWith(item, 'TEST getIDHrefObs');
expect(out).toBe('TEST createOnEndpoint');
});
}); });
}); });
}); });

View File

@@ -1,147 +1,62 @@
/* eslint-disable max-classes-per-file */ /* eslint-disable max-classes-per-file */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ItemDataService } from './item-data.service'; import { BaseItemDataService } from './item-data.service';
import { UpdateDataService } from './update-data.service';
import { Item } from '../shared/item.model'; import { Item } from '../shared/item.model';
import { RestRequestMethod } from './rest-request-method';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { BrowseService } from '../browse/browse.service'; import { BrowseService } from '../browse/browse.service';
import { CollectionDataService } from './collection-data.service'; import { CollectionDataService } from './collection-data.service';
import { map, switchMap } from 'rxjs/operators'; import { switchMap } from 'rxjs/operators';
import { BundleDataService } from './bundle-data.service'; import { BundleDataService } from './bundle-data.service';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { NoContent } from '../shared/NoContent.model'; import { IdentifiableDataService } from './base/identifiable-data.service';
import { hasValue } from '../../shared/empty.util'; import { CreateDataImpl } from './base/create-data';
import { Operation } from 'fast-json-patch';
import { getFirstCompletedRemoteData } from '../shared/operators';
import { CoreState } from '../core-state.model';
/** /**
* A custom implementation of the ItemDataService, but for collection item templates * Data service for interacting with Item templates via their Collection
* Makes sure to change the endpoint before sending out CRUD requests for the item template
*/ */
class DataServiceImpl extends ItemDataService { class CollectionItemTemplateDataService extends IdentifiableDataService<Item> {
protected collectionLinkPath = 'itemtemplate'; private createData: CreateDataImpl<Item>;
protected linkPath = 'itemtemplates';
/**
* Endpoint dynamically changing depending on what request we're sending
*/
private endpoint$: Observable<string>;
/**
* Is the current endpoint based on a collection?
*/
private collectionEndpoint = false;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected bs: BrowseService,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected http: HttpClient, protected collectionService: CollectionDataService,
protected comparator: DSOChangeAnalyzer<Item>, ) {
protected bundleService: BundleDataService, super('itemtemplates', requestService, rdbService, objectCache, halService, undefined);
protected collectionService: CollectionDataService) {
super(requestService, rdbService, store, bs, objectCache, halService, notificationsService, http, comparator, bundleService); // We only intend to use createOnEndpoint, so this inner data service feature doesn't need an endpoint at all
this.createData = new CreateDataImpl<Item>(undefined, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive);
} }
/** /**
* Get the endpoint based on a collection * Create an observable for the HREF of a specific object based on its identifier
* @param collectionID The ID of the collection to base the endpoint on *
* Overridden to ensure that {@link findById} works with Collection IDs and points to the template.
* @param collectionID the ID of a Collection
*/ */
public getCollectionEndpoint(collectionID: string): Observable<string> { public getIDHrefObs(collectionID: string): Observable<string> {
return this.collectionService.getIDHrefObs(collectionID).pipe( return this.collectionService.getIDHrefObs(collectionID).pipe(
switchMap((href: string) => this.halService.getEndpoint(this.collectionLinkPath, href)) switchMap((href: string) => this.halService.getEndpoint('itemtemplate', href)),
); );
} }
/** /**
* Set the endpoint to be based on a collection * Create a new item template for a Collection by ID
* @param collectionID The ID of the collection to base the endpoint on
*/
private setCollectionEndpoint(collectionID: string) {
this.collectionEndpoint = true;
this.endpoint$ = this.getCollectionEndpoint(collectionID);
}
/**
* Set the endpoint to the regular linkPath
*/
private setRegularEndpoint() {
this.collectionEndpoint = false;
this.endpoint$ = this.halService.getEndpoint(this.linkPath);
}
/**
* Get the base endpoint for all requests
* Uses the current collectionID to assemble a request endpoint for the collection's item template
*/
protected getEndpoint(): Observable<string> {
return this.endpoint$;
}
/**
* If the current endpoint is based on a collection, simply return the collection's template endpoint, otherwise
* create a regular template endpoint
* @param resourceID
*/
getIDHrefObs(resourceID: string): Observable<string> {
if (this.collectionEndpoint) {
return this.getEndpoint();
} else {
return super.getIDHrefObs(resourceID);
}
}
/**
* Set the collection ID and send a find by ID request
* @param collectionID
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
findByCollectionID(collectionID: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<Item>[]): Observable<RemoteData<Item>> {
this.setCollectionEndpoint(collectionID);
return super.findById(collectionID, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Set the collection ID and send a create request
* @param item * @param item
* @param collectionID * @param collectionID
*/ */
createTemplate(item: Item, collectionID: string): Observable<RemoteData<Item>> { public createTemplate(item: Item, collectionID: string): Observable<RemoteData<Item>> {
this.setCollectionEndpoint(collectionID); return this.createData.createOnEndpoint(item, this.getIDHrefObs(collectionID));
return super.create(item);
}
/**
* Set the collection ID and send a delete request
* @param item
* @param collectionID
*/
deleteByCollectionID(item: Item, collectionID: string): Observable<boolean> {
this.setRegularEndpoint();
return super.delete(item.uuid).pipe(
getFirstCompletedRemoteData(),
map((response: RemoteData<NoContent>) => hasValue(response) && response.hasSucceeded)
);
} }
} }
@@ -149,43 +64,23 @@ class DataServiceImpl extends ItemDataService {
* A service responsible for fetching/sending data from/to the REST API on a collection's itemtemplates endpoint * A service responsible for fetching/sending data from/to the REST API on a collection's itemtemplates endpoint
*/ */
@Injectable() @Injectable()
export class ItemTemplateDataService implements UpdateDataService<Item> { export class ItemTemplateDataService extends BaseItemDataService {
/** private byCollection: CollectionItemTemplateDataService;
* The data service responsible for all CRUD actions on the item
*/
private dataService: DataServiceImpl;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected bs: BrowseService,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DSOChangeAnalyzer<Item>, protected comparator: DSOChangeAnalyzer<Item>,
protected browseService: BrowseService,
protected bundleService: BundleDataService, protected bundleService: BundleDataService,
protected collectionService: CollectionDataService) { protected collectionService: CollectionDataService,
this.dataService = new DataServiceImpl(requestService, rdbService, store, bs, objectCache, halService, notificationsService, http, comparator, bundleService, collectionService); ) {
} super('itemtemplates', requestService, rdbService, objectCache, halService, notificationsService, comparator, browseService, bundleService);
/** this.byCollection = new CollectionItemTemplateDataService(requestService, rdbService, objectCache, halService, notificationsService, collectionService);
* Commit current object changes to the server
*/
commitUpdates(method?: RestRequestMethod) {
this.dataService.commitUpdates(method);
}
/**
* Add a new patch to the object cache
*/
update(object: Item): Observable<RemoteData<Item>> {
return this.dataService.update(object);
}
patch(dso: Item, operations: Operation[]): Observable<RemoteData<Item>> {
return this.dataService.patch(dso, operations);
} }
/** /**
@@ -199,7 +94,7 @@ export class ItemTemplateDataService implements UpdateDataService<Item> {
* {@link HALLink}s should be automatically resolved * {@link HALLink}s should be automatically resolved
*/ */
findByCollectionID(collectionID: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<Item>[]): Observable<RemoteData<Item>> { findByCollectionID(collectionID: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<Item>[]): Observable<RemoteData<Item>> {
return this.dataService.findByCollectionID(collectionID, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); return this.byCollection.findById(collectionID, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
} }
/** /**
@@ -207,17 +102,8 @@ export class ItemTemplateDataService implements UpdateDataService<Item> {
* @param item * @param item
* @param collectionID * @param collectionID
*/ */
create(item: Item, collectionID: string): Observable<RemoteData<Item>> { createByCollectionID(item: Item, collectionID: string): Observable<RemoteData<Item>> {
return this.dataService.createTemplate(item, collectionID); return this.byCollection.createTemplate(item, collectionID);
}
/**
* Delete a template item by collection ID
* @param item
* @param collectionID
*/
deleteByCollectionID(item: Item, collectionID: string): Observable<boolean> {
return this.dataService.deleteByCollectionID(item, collectionID);
} }
/** /**
@@ -225,6 +111,6 @@ export class ItemTemplateDataService implements UpdateDataService<Item> {
* @param collectionID The ID of the collection to base the endpoint on * @param collectionID The ID of the collection to base the endpoint on
*/ */
getCollectionEndpoint(collectionID: string): Observable<string> { getCollectionEndpoint(collectionID: string): Observable<string> {
return this.dataService.getCollectionEndpoint(collectionID); return this.byCollection.getIDHrefObs(collectionID);
} }
} }

View File

@@ -10,6 +10,7 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
import { RequestParam } from '../cache/models/request-param.model'; import { RequestParam } from '../cache/models/request-param.model';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
import { createPaginatedList } from '../../shared/testing/utils.test';
describe('MetadataFieldDataService', () => { describe('MetadataFieldDataService', () => {
let metadataFieldService: MetadataFieldDataService; let metadataFieldService: MetadataFieldDataService;
@@ -33,16 +34,19 @@ describe('MetadataFieldDataService', () => {
generateRequestId: '34cfed7c-f597-49ef-9cbe-ea351f0023c2', generateRequestId: '34cfed7c-f597-49ef-9cbe-ea351f0023c2',
send: {}, send: {},
getByUUID: observableOf({ response: new RestResponse(true, 200, 'OK') }), getByUUID: observableOf({ response: new RestResponse(true, 200, 'OK') }),
setStaleByHrefSubstring: {} setStaleByHrefSubstring: {},
}); });
halService = Object.assign(new HALEndpointServiceStub(endpoint)); halService = Object.assign(new HALEndpointServiceStub(endpoint));
notificationsService = jasmine.createSpyObj('notificationsService', { notificationsService = jasmine.createSpyObj('notificationsService', {
error: {} error: {},
}); });
rdbService = jasmine.createSpyObj('rdbService', { rdbService = jasmine.createSpyObj('rdbService', {
buildSingle: createSuccessfulRemoteDataObject$(undefined) buildSingle: createSuccessfulRemoteDataObject$(undefined),
buildList: createSuccessfulRemoteDataObject$(createPaginatedList([])),
}); });
metadataFieldService = new MetadataFieldDataService(requestService, rdbService, undefined, halService, undefined, undefined, undefined, notificationsService); metadataFieldService = new MetadataFieldDataService(
requestService, rdbService, undefined, halService, notificationsService,
);
} }
beforeEach(() => { beforeEach(() => {

View File

@@ -1,17 +1,11 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { hasValue } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { DataService } from './data.service';
import { PaginatedList } from './paginated-list.model'; import { PaginatedList } from './paginated-list.model';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { HttpClient } from '@angular/common/http';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { METADATA_FIELD } from '../metadata/metadata-field.resource-type'; import { METADATA_FIELD } from '../metadata/metadata-field.resource-type';
import { MetadataField } from '../metadata/metadata-field.model'; import { MetadataField } from '../metadata/metadata-field.model';
import { MetadataSchema } from '../metadata/metadata-schema.model'; import { MetadataSchema } from '../metadata/metadata-schema.model';
@@ -19,29 +13,43 @@ import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { RequestParam } from '../cache/models/request-param.model'; import { RequestParam } from '../cache/models/request-param.model';
import { CoreState } from '../core-state.model';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
import { SearchData, SearchDataImpl } from './base/search-data';
import { PutData, PutDataImpl } from './base/put-data';
import { CreateData, CreateDataImpl } from './base/create-data';
import { NoContent } from '../shared/NoContent.model';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { DeleteData, DeleteDataImpl } from './base/delete-data';
import { IdentifiableDataService } from './base/identifiable-data.service';
/** /**
* A service responsible for fetching/sending data from/to the REST API on the metadatafields endpoint * A service responsible for fetching/sending data from/to the REST API on the metadatafields endpoint
*/ */
@Injectable() @Injectable()
@dataService(METADATA_FIELD) @dataService(METADATA_FIELD)
export class MetadataFieldDataService extends DataService<MetadataField> { export class MetadataFieldDataService extends IdentifiableDataService<MetadataField> implements CreateData<MetadataField>, PutData<MetadataField>, DeleteData<MetadataField>, SearchData<MetadataField> {
protected linkPath = 'metadatafields'; private createData: CreateData<MetadataField>;
private searchData: SearchData<MetadataField>;
private putData: PutData<MetadataField>;
private deleteData: DeleteData<MetadataField>;
protected searchBySchemaLinkPath = 'bySchema'; protected searchBySchemaLinkPath = 'bySchema';
protected searchByFieldNameLinkPath = 'byFieldName'; protected searchByFieldNameLinkPath = 'byFieldName';
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected halService: HALEndpointService,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected comparator: DefaultChangeAnalyzer<MetadataField>, protected halService: HALEndpointService,
protected http: HttpClient, protected notificationsService: NotificationsService,
protected notificationsService: NotificationsService) { ) {
super(); super('metadatafields', requestService, rdbService, objectCache, halService);
this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive);
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
this.putData = new PutDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
} }
/** /**
@@ -57,7 +65,7 @@ export class MetadataFieldDataService extends DataService<MetadataField> {
*/ */
findBySchema(schema: MetadataSchema, options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<MetadataField>[]) { findBySchema(schema: MetadataSchema, options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<MetadataField>[]) {
const optionsWithSchema = Object.assign(new FindListOptions(), options, { const optionsWithSchema = Object.assign(new FindListOptions(), options, {
searchParams: [new RequestParam('schema', schema.prefix)] searchParams: [new RequestParam('schema', schema.prefix)],
}); });
return this.searchBy(this.searchBySchemaLinkPath, optionsWithSchema, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); return this.searchBy(this.searchBySchemaLinkPath, optionsWithSchema, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
} }
@@ -85,8 +93,8 @@ export class MetadataFieldDataService extends DataService<MetadataField> {
new RequestParam('element', hasValue(element) ? element : ''), new RequestParam('element', hasValue(element) ? element : ''),
new RequestParam('qualifier', hasValue(qualifier) ? qualifier : ''), new RequestParam('qualifier', hasValue(qualifier) ? qualifier : ''),
new RequestParam('query', hasValue(query) ? query : ''), new RequestParam('query', hasValue(query) ? query : ''),
new RequestParam('exactName', hasValue(exactName) ? exactName : '') new RequestParam('exactName', hasValue(exactName) ? exactName : ''),
] ],
}); });
return this.searchBy(this.searchByFieldNameLinkPath, optionParams, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); return this.searchBy(this.searchByFieldNameLinkPath, optionParams, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
} }
@@ -112,4 +120,80 @@ export class MetadataFieldDataService extends DataService<MetadataField> {
} }
/**
* Delete an existing object on the server
* @param objectId The id of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
*/
delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.delete(objectId, copyVirtualMetadata);
}
/**
* Delete an existing object on the server
* @param href The self link of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
* Only emits once all request related to the DSO has been invalidated.
*/
public deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
}
/**
* Send a PUT request for the specified object
*
* @param object The object to send a put request for.
*/
put(object: MetadataField): Observable<RemoteData<MetadataField>> {
return this.putData.put(object);
}
/**
* Create a new object on the server, and store the response in the object cache
*
* @param object The object to create
* @param params Array with additional params to combine with query string
*/
create(object: MetadataField, ...params: RequestParam[]): Observable<RemoteData<MetadataField>> {
return this.createData.create(object, ...params);
}
/**
* Create the HREF for a specific object's search method with given options object
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @return {Observable<string>}
* Return an observable that emits created HREF
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
*/
public getSearchByHref(searchMethod: string, options?: FindListOptions, ...linksToFollow: FollowLinkConfig<MetadataField>[]): Observable<string> {
return this.searchData.getSearchByHref(searchMethod, options, ...linksToFollow);
}
/**
* Make a new FindListRequest with given search method
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>}
* Return an observable that emits response from the server
*/
public searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<MetadataField>[]): Observable<RemoteData<PaginatedList<MetadataField>>> {
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
} }

View File

@@ -24,14 +24,20 @@ describe('MetadataSchemaDataService', () => {
generateRequestId: '34cfed7c-f597-49ef-9cbe-ea351f0023c2', generateRequestId: '34cfed7c-f597-49ef-9cbe-ea351f0023c2',
send: {}, send: {},
getByUUID: observableOf({ response: new RestResponse(true, 200, 'OK') }), getByUUID: observableOf({ response: new RestResponse(true, 200, 'OK') }),
removeByHrefSubstring: {} removeByHrefSubstring: {},
}); });
halService = Object.assign(new HALEndpointServiceStub(endpoint)); halService = Object.assign(new HALEndpointServiceStub(endpoint));
notificationsService = jasmine.createSpyObj('notificationsService', { notificationsService = jasmine.createSpyObj('notificationsService', {
error: {} error: {},
}); });
rdbService = getMockRemoteDataBuildService(); rdbService = getMockRemoteDataBuildService();
metadataSchemaService = new MetadataSchemaDataService(requestService, rdbService, undefined, halService, undefined, undefined, undefined, notificationsService); metadataSchemaService = new MetadataSchemaDataService(
requestService,
rdbService,
null,
halService,
notificationsService,
);
} }
beforeEach(() => { beforeEach(() => {

View File

@@ -1,6 +1,4 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
@@ -8,33 +6,45 @@ import { ObjectCacheService } from '../cache/object-cache.service';
import { MetadataSchema } from '../metadata/metadata-schema.model'; import { MetadataSchema } from '../metadata/metadata-schema.model';
import { METADATA_SCHEMA } from '../metadata/metadata-schema.resource-type'; import { METADATA_SCHEMA } from '../metadata/metadata-schema.resource-type';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { DataService } from './data.service';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { hasValue } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { CoreState } from '../core-state.model'; import { PutData, PutDataImpl } from './base/put-data';
import { CreateData, CreateDataImpl } from './base/create-data';
import { NoContent } from '../shared/NoContent.model';
import { FindAllData, FindAllDataImpl } from './base/find-all-data';
import { FindListOptions } from './find-list-options.model';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { PaginatedList } from './paginated-list.model';
import { IdentifiableDataService } from './base/identifiable-data.service';
import { DeleteData, DeleteDataImpl } from './base/delete-data';
/** /**
* A service responsible for fetching/sending data from/to the REST API on the metadataschemas endpoint * A service responsible for fetching/sending data from/to the REST API on the metadataschemas endpoint
*/ */
@Injectable() @Injectable()
@dataService(METADATA_SCHEMA) @dataService(METADATA_SCHEMA)
export class MetadataSchemaDataService extends DataService<MetadataSchema> { export class MetadataSchemaDataService extends IdentifiableDataService<MetadataSchema> implements FindAllData<MetadataSchema>, DeleteData<MetadataSchema> {
protected linkPath = 'metadataschemas'; private createData: CreateData<MetadataSchema>;
private findAllData: FindAllData<MetadataSchema>;
private putData: PutData<MetadataSchema>;
private deleteData: DeleteData<MetadataSchema>;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected halService: HALEndpointService,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected comparator: DefaultChangeAnalyzer<MetadataSchema>, protected halService: HALEndpointService,
protected http: HttpClient, protected notificationsService: NotificationsService,
protected notificationsService: NotificationsService) { ) {
super(); super('metadataschemas', requestService, rdbService, objectCache, halService);
this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive);
this.putData = new PutDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
} }
/** /**
@@ -49,9 +59,9 @@ export class MetadataSchemaDataService extends DataService<MetadataSchema> {
const isUpdate = hasValue(schema.id); const isUpdate = hasValue(schema.id);
if (isUpdate) { if (isUpdate) {
return this.put(schema); return this.putData.put(schema);
} else { } else {
return this.create(schema); return this.createData.create(schema);
} }
} }
@@ -61,8 +71,50 @@ export class MetadataSchemaDataService extends DataService<MetadataSchema> {
*/ */
clearRequests(): Observable<string> { clearRequests(): Observable<string> {
return this.getBrowseEndpoint().pipe( return this.getBrowseEndpoint().pipe(
tap((href: string) => this.requestService.removeByHrefSubstring(href)) tap((href: string) => this.requestService.removeByHrefSubstring(href)),
); );
} }
/**
* 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 useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>>}
* Return an observable that emits object list
*/
public findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<MetadataSchema>[]): Observable<RemoteData<PaginatedList<MetadataSchema>>> {
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Delete an existing object on the server
* @param objectId The id of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
*/
public delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.delete(objectId, copyVirtualMetadata);
}
/**
* Delete an existing object on the server
* @param href The self link of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
* Only emits once all request related to the DSO has been invalidated.
*/
public deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
}
} }

View File

@@ -0,0 +1,28 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
import { BaseDataService } from './base/base-data.service';
import { RequestService } from './request.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 { IdentifiableDataService } from './base/identifiable-data.service';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { Observable } from 'rxjs';
import { RemoteData } from './remote-data';
import { DSpaceObject } from '../shared/dspace-object.model';
export class PersistentIdentifierDataService extends IdentifiableDataService<DSpaceObject> {
constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
) {
super('pid', requestService, rdbService, objectCache, halService);
}
}

View File

@@ -1,13 +1,8 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { DataService } from '../data.service';
import { RequestService } from '../request.service'; import { RequestService } from '../request.service';
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../../cache/object-cache.service'; import { ObjectCacheService } from '../../cache/object-cache.service';
import { HALEndpointService } from '../../shared/hal-endpoint.service'; import { HALEndpointService } from '../../shared/hal-endpoint.service';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DefaultChangeAnalyzer } from '../default-change-analyzer.service';
import { Process } from '../../../process-page/processes/process.model'; import { Process } from '../../../process-page/processes/process.model';
import { dataService } from '../../cache/builders/build-decorators'; import { dataService } from '../../cache/builders/build-decorators';
import { PROCESS } from '../../../process-page/processes/process.resource-type'; import { PROCESS } from '../../../process-page/processes/process.resource-type';
@@ -17,24 +12,26 @@ import { PaginatedList } from '../paginated-list.model';
import { Bitstream } from '../../shared/bitstream.model'; import { Bitstream } from '../../shared/bitstream.model';
import { RemoteData } from '../remote-data'; import { RemoteData } from '../remote-data';
import { BitstreamDataService } from '../bitstream-data.service'; import { BitstreamDataService } from '../bitstream-data.service';
import { CoreState } from '../../core-state.model'; import { IdentifiableDataService } from '../base/identifiable-data.service';
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
import { FindAllData, FindAllDataImpl } from '../base/find-all-data';
import { FindListOptions } from '../find-list-options.model';
@Injectable() @Injectable()
@dataService(PROCESS) @dataService(PROCESS)
export class ProcessDataService extends DataService<Process> { export class ProcessDataService extends IdentifiableDataService<Process> implements FindAllData<Process> {
protected linkPath = 'processes'; private findAllData: FindAllData<Process>;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected bitstreamDataService: BitstreamDataService, protected bitstreamDataService: BitstreamDataService,
protected http: HttpClient, ) {
protected comparator: DefaultChangeAnalyzer<Process>) { super('processes', requestService, rdbService, objectCache, halService);
super();
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
} }
/** /**
@@ -55,4 +52,22 @@ export class ProcessDataService extends DataService<Process> {
const href$ = this.getFilesEndpoint(processId); const href$ = this.getFilesEndpoint(processId);
return this.bitstreamDataService.findAllByHref(href$); return this.bitstreamDataService.findAllByHref(href$);
} }
/**
* 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 useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>>}
* Return an observable that emits object list
*/
findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<Process>[]): Observable<RemoteData<PaginatedList<Process>>> {
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
} }

View File

@@ -1,18 +1,13 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { DataService } from '../data.service';
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../../cache/object-cache.service'; import { ObjectCacheService } from '../../cache/object-cache.service';
import { HALEndpointService } from '../../shared/hal-endpoint.service'; import { HALEndpointService } from '../../shared/hal-endpoint.service';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DefaultChangeAnalyzer } from '../default-change-analyzer.service';
import { Script } from '../../../process-page/scripts/script.model'; import { Script } from '../../../process-page/scripts/script.model';
import { ProcessParameter } from '../../../process-page/processes/process-parameter.model'; import { ProcessParameter } from '../../../process-page/processes/process-parameter.model';
import { map, take } from 'rxjs/operators'; import { map, take } from 'rxjs/operators';
import { URLCombiner } from '../../url-combiner/url-combiner'; import { URLCombiner } from '../../url-combiner/url-combiner';
import { RemoteData } from '../remote-data'; import { RemoteData } from '../remote-data';
import { MultipartPostRequest} from '../request.models'; import { MultipartPostRequest } from '../request.models';
import { RequestService } from '../request.service'; import { RequestService } from '../request.service';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { dataService } from '../../cache/builders/build-decorators'; import { dataService } from '../../cache/builders/build-decorators';
@@ -21,26 +16,29 @@ import { Process } from '../../../process-page/processes/process.model';
import { hasValue } from '../../../shared/empty.util'; import { hasValue } from '../../../shared/empty.util';
import { getFirstCompletedRemoteData } from '../../shared/operators'; import { getFirstCompletedRemoteData } from '../../shared/operators';
import { RestRequest } from '../rest-request.model'; import { RestRequest } from '../rest-request.model';
import { CoreState } from '../../core-state.model'; import { IdentifiableDataService } from '../base/identifiable-data.service';
import { FindAllData, FindAllDataImpl } from '../base/find-all-data';
import { FindListOptions } from '../find-list-options.model';
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
import { PaginatedList } from '../paginated-list.model';
export const METADATA_IMPORT_SCRIPT_NAME = 'metadata-import'; export const METADATA_IMPORT_SCRIPT_NAME = 'metadata-import';
export const METADATA_EXPORT_SCRIPT_NAME = 'metadata-export'; export const METADATA_EXPORT_SCRIPT_NAME = 'metadata-export';
@Injectable() @Injectable()
@dataService(SCRIPT) @dataService(SCRIPT)
export class ScriptDataService extends DataService<Script> { export class ScriptDataService extends IdentifiableDataService<Script> implements FindAllData<Script> {
protected linkPath = 'scripts'; private findAllData: FindAllDataImpl<Script>;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, ) {
protected http: HttpClient, super('scripts', requestService, rdbService, objectCache, halService);
protected comparator: DefaultChangeAnalyzer<Script>) {
super(); this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
} }
public invoke(scriptName: string, parameters: ProcessParameter[], files: File[]): Observable<RemoteData<Process>> { public invoke(scriptName: string, parameters: ProcessParameter[], files: File[]): Observable<RemoteData<Process>> {
@@ -75,6 +73,25 @@ export class ScriptDataService extends DataService<Script> {
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
map((rd: RemoteData<Script>) => { map((rd: RemoteData<Script>) => {
return hasValue(rd.payload); return hasValue(rd.payload);
})); }),
);
}
/**
* 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 useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>>}
* Return an observable that emits object list
*/
public findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<Script>[]): Observable<RemoteData<PaginatedList<Script>>> {
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
} }
} }

View File

@@ -2,17 +2,14 @@ import { of as observableOf } from 'rxjs';
import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock'; import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock';
import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { getMockRequestService } from '../../shared/mocks/request.service.mock';
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub';
import { import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
createSuccessfulRemoteDataObject,
createSuccessfulRemoteDataObject$
} from '../../shared/remote-data.utils';
import { ObjectCacheService } from '../cache/object-cache.service';
import { ItemType } from '../shared/item-relationships/item-type.model'; import { ItemType } from '../shared/item-relationships/item-type.model';
import { RelationshipType } from '../shared/item-relationships/relationship-type.model'; import { RelationshipType } from '../shared/item-relationships/relationship-type.model';
import { RelationshipTypeService } from './relationship-type.service'; import { RelationshipTypeService } from './relationship-type.service';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { createPaginatedList } from '../../shared/testing/utils.test'; import { createPaginatedList } from '../../shared/testing/utils.test';
import { hasValueOperator } from '../../shared/empty.util'; import { hasValueOperator } from '../../shared/empty.util';
import { ObjectCacheService } from '../cache/object-cache.service';
describe('RelationshipTypeService', () => { describe('RelationshipTypeService', () => {
let service: RelationshipTypeService; let service: RelationshipTypeService;
@@ -29,7 +26,6 @@ describe('RelationshipTypeService', () => {
let relationshipType1; let relationshipType1;
let relationshipType2; let relationshipType2;
let itemService;
let buildList; let buildList;
let rdbService; let rdbService;
let objectCache; let objectCache;
@@ -72,22 +68,14 @@ describe('RelationshipTypeService', () => {
/* eslint-enable no-empty, @typescript-eslint/no-empty-function */ /* eslint-enable no-empty, @typescript-eslint/no-empty-function */
}) as ObjectCacheService; }) as ObjectCacheService;
itemService = undefined;
} }
function initTestService() { function initTestService() {
return new RelationshipTypeService( return new RelationshipTypeService(
itemService,
requestService, requestService,
rdbService, rdbService,
null,
halService,
objectCache, objectCache,
null, halService,
null,
null,
null
); );
} }

View File

@@ -1,28 +1,22 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
import { map, mergeMap, switchMap, toArray } from 'rxjs/operators'; import { map, mergeMap, switchMap, toArray } from 'rxjs/operators';
import { AppState } from '../../app.reducer';
import { hasValue } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { followLink } from '../../shared/utils/follow-link-config.model';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.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 { HALEndpointService } from '../shared/hal-endpoint.service';
import { ItemType } from '../shared/item-relationships/item-type.model'; import { ItemType } from '../shared/item-relationships/item-type.model';
import { RelationshipType } from '../shared/item-relationships/relationship-type.model'; import { RelationshipType } from '../shared/item-relationships/relationship-type.model';
import { RELATIONSHIP_TYPE } from '../shared/item-relationships/relationship-type.resource-type'; import { RELATIONSHIP_TYPE } from '../shared/item-relationships/relationship-type.resource-type';
import { getFirstSucceededRemoteData, getFirstCompletedRemoteData, getRemoteDataPayload } from '../shared/operators'; import { getFirstCompletedRemoteData, getFirstSucceededRemoteData, getRemoteDataPayload } from '../shared/operators';
import { DataService } from './data.service';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { ItemDataService } from './item-data.service';
import { PaginatedList } from './paginated-list.model'; import { PaginatedList } from './paginated-list.model';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { CoreState } from '../core-state.model'; import { BaseDataService } from './base/base-data.service';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FindAllDataImpl } from './base/find-all-data';
import { SearchDataImpl } from './base/search-data';
import { ObjectCacheService } from '../cache/object-cache.service';
/** /**
* Check if one side of a RelationshipType is the ItemType with the given label * Check if one side of a RelationshipType is the ItemType with the given label
@@ -38,20 +32,20 @@ const checkSide = (typeRd: RemoteData<ItemType>, label: string): boolean =>
*/ */
@Injectable() @Injectable()
@dataService(RELATIONSHIP_TYPE) @dataService(RELATIONSHIP_TYPE)
export class RelationshipTypeService extends DataService<RelationshipType> { export class RelationshipTypeService extends BaseDataService<RelationshipType> {
protected linkPath = 'relationshiptypes'; private searchData: SearchDataImpl<RelationshipType>;
private findAllData: FindAllDataImpl<RelationshipType>;
constructor(protected itemService: ItemDataService, constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected objectCache: ObjectCacheService, ) {
protected notificationsService: NotificationsService, super('relationshiptypes', requestService, rdbService, objectCache, halService);
protected http: HttpClient,
protected comparator: DefaultChangeAnalyzer<RelationshipType>, this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
protected appStore: Store<AppState>) { this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
super();
} }
/** /**
@@ -67,34 +61,34 @@ export class RelationshipTypeService extends DataService<RelationshipType> {
*/ */
getRelationshipTypeByLabelAndTypes(relationshipTypeLabel: string, firstItemType: string, secondItemType: string): Observable<RelationshipType> { getRelationshipTypeByLabelAndTypes(relationshipTypeLabel: string, firstItemType: string, secondItemType: string): Observable<RelationshipType> {
// Retrieve all relationship types from the server in a single page // Retrieve all relationship types from the server in a single page
return this.findAll({ currentPage: 1, elementsPerPage: 9999 }, true, true, followLink('leftType'), followLink('rightType')) return this.findAllData.findAll({ currentPage: 1, elementsPerPage: 9999 }, true, true, followLink('leftType'), followLink('rightType'))
.pipe( .pipe(
getFirstSucceededRemoteData(), getFirstSucceededRemoteData(),
// Emit each type in the page array separately // Emit each type in the page array separately
switchMap((typeListRD: RemoteData<PaginatedList<RelationshipType>>) => typeListRD.payload.page), switchMap((typeListRD: RemoteData<PaginatedList<RelationshipType>>) => typeListRD.payload.page),
// Check each type individually, to see if it matches the provided types // Check each type individually, to see if it matches the provided types
mergeMap((relationshipType: RelationshipType) => { mergeMap((relationshipType: RelationshipType) => {
if (relationshipType.leftwardType === relationshipTypeLabel) { if (relationshipType.leftwardType === relationshipTypeLabel) {
return this.checkType(relationshipType, firstItemType, secondItemType); return this.checkType(relationshipType, firstItemType, secondItemType);
} else if (relationshipType.rightwardType === relationshipTypeLabel) { } else if (relationshipType.rightwardType === relationshipTypeLabel) {
return this.checkType(relationshipType, secondItemType, firstItemType); return this.checkType(relationshipType, secondItemType, firstItemType);
} else { } else {
return [null]; return [null];
} }
}), }),
// Wait for all types to be checked and emit once, with the results combined back into an // Wait for all types to be checked and emit once, with the results combined back into an
// array // array
toArray(), toArray(),
// Look for a match in the array and emit it if found, or null if one isn't found // Look for a match in the array and emit it if found, or null if one isn't found
map((types: RelationshipType[]) => { map((types: RelationshipType[]) => {
const match = types.find((type: RelationshipType) => hasValue(type)); const match = types.find((type: RelationshipType) => hasValue(type));
if (hasValue(match)) { if (hasValue(match)) {
return match; return match;
} else { } else {
return null; return null;
} }
}) }),
); );
} }
/** /**
@@ -127,27 +121,29 @@ export class RelationshipTypeService extends DataService<RelationshipType> {
* Returns an observable of the given RelationshipType if it matches, null if it doesn't * Returns an observable of the given RelationshipType if it matches, null if it doesn't
* *
* @param type The RelationshipType to check * @param type The RelationshipType to check
* @param useCachedVersionIfAvailable
* @param reRequestOnStale
* @param linksToFollow
*/ */
searchByEntityType(type: string,useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<RelationshipType>[]): Observable<PaginatedList<RelationshipType>> { searchByEntityType(type: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<RelationshipType>[]): Observable<PaginatedList<RelationshipType>> {
return this.searchData.searchBy(
return this.searchBy(
'byEntityType', 'byEntityType',
{ {
searchParams: [ searchParams: [
{ {
fieldName: 'type', fieldName: 'type',
fieldValue: type fieldValue: type,
}, },
{ {
fieldName: 'size', fieldName: 'size',
fieldValue: 100 fieldValue: 100,
}, },
] ],
}, useCachedVersionIfAvailable,reRequestOnStale,...linksToFollow).pipe( }, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow,
).pipe(
getFirstSucceededRemoteData(), getFirstSucceededRemoteData(),
getRemoteDataPayload(), getRemoteDataPayload(),
) as Observable<PaginatedList<RelationshipType>>; ) as Observable<PaginatedList<RelationshipType>>;
} }
} }

View File

@@ -6,7 +6,7 @@ import { Relationship } from '../shared/item-relationships/relationship.model';
import { Item } from '../shared/item.model'; import { Item } from '../shared/item.model';
import { PageInfo } from '../shared/page-info.model'; import { PageInfo } from '../shared/page-info.model';
import { buildPaginatedList } from './paginated-list.model'; import { buildPaginatedList } from './paginated-list.model';
import { DeleteRequest} from './request.models'; import { DeleteRequest } from './request.models';
import { RelationshipService } from './relationship.service'; import { RelationshipService } from './relationship.service';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub';
@@ -123,15 +123,11 @@ describe('RelationshipService', () => {
function initTestService() { function initTestService() {
return new RelationshipService( return new RelationshipService(
itemService,
requestService, requestService,
rdbService, rdbService,
null,
halService, halService,
objectCache, objectCache,
null, itemService,
null,
null,
null, null,
jasmine.createSpy('paginatedRelationsToItems').and.returnValue((v) => v), jasmine.createSpy('paginatedRelationsToItems').and.returnValue((v) => v),
); );

View File

@@ -1,4 +1,4 @@
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { HttpHeaders } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core'; import { Inject, Injectable } from '@angular/core';
import { MemoizedSelector, select, Store } from '@ngrx/store'; import { MemoizedSelector, select, Store } from '@ngrx/store';
import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
@@ -15,7 +15,6 @@ import {
SetNameVariantAction SetNameVariantAction
} from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.actions'; } from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.actions';
import { NameVariantListState } from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.reducer'; import { NameVariantListState } from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.reducer';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
@@ -33,19 +32,19 @@ import {
getFirstSucceededRemoteDataPayload, getFirstSucceededRemoteDataPayload,
getRemoteDataPayload getRemoteDataPayload
} from '../shared/operators'; } from '../shared/operators';
import { DataService } from './data.service';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { ItemDataService } from './item-data.service'; import { ItemDataService } from './item-data.service';
import { PaginatedList } from './paginated-list.model'; import { PaginatedList } from './paginated-list.model';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { DeleteRequest, PostRequest} from './request.models'; import { DeleteRequest, PostRequest } from './request.models';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { NoContent } from '../shared/NoContent.model'; import { NoContent } from '../shared/NoContent.model';
import { RequestEntryState } from './request-entry-state.model'; import { RequestEntryState } from './request-entry-state.model';
import { sendRequest } from '../shared/request.operators'; import { sendRequest } from '../shared/request.operators';
import { RestRequest } from './rest-request.model'; import { RestRequest } from './rest-request.model';
import { CoreState } from '../core-state.model';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
import { SearchData, SearchDataImpl } from './base/search-data';
import { PutData, PutDataImpl } from './base/put-data';
import { IdentifiableDataService } from './base/identifiable-data.service';
const relationshipListsStateSelector = (state: AppState) => state.relationshipLists; const relationshipListsStateSelector = (state: AppState) => state.relationshipLists;
@@ -75,22 +74,23 @@ const compareItemsByUUID = (itemCheck: Item) =>
*/ */
@Injectable() @Injectable()
@dataService(RELATIONSHIP) @dataService(RELATIONSHIP)
export class RelationshipService extends DataService<Relationship> { export class RelationshipService extends IdentifiableDataService<Relationship> implements SearchData<Relationship> {
protected linkPath = 'relationships'; private searchData: SearchData<Relationship>;
protected responseMsToLive = 15 * 60 * 1000; private putData: PutData<Relationship>;
constructor(protected itemService: ItemDataService, constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>, protected halService: HALEndpointService,
protected halService: HALEndpointService, protected objectCache: ObjectCacheService,
protected objectCache: ObjectCacheService, protected itemService: ItemDataService,
protected notificationsService: NotificationsService, protected appStore: Store<AppState>,
protected http: HttpClient, @Inject(PAGINATED_RELATIONS_TO_ITEMS_OPERATOR) private paginatedRelationsToItems: (thisId: string) => (source: Observable<RemoteData<PaginatedList<Relationship>>>) => Observable<RemoteData<PaginatedList<Item>>>,
protected comparator: DefaultChangeAnalyzer<Relationship>, ) {
protected appStore: Store<AppState>, super('relationships', requestService, rdbService, objectCache, halService, 15 * 60 * 1000);
@Inject(PAGINATED_RELATIONS_TO_ITEMS_OPERATOR) private paginatedRelationsToItems: (thisId: string) => (source: Observable<RemoteData<PaginatedList<Relationship>>>) => Observable<RemoteData<PaginatedList<Item>>>) {
super(); this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
this.putData = new PutDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
} }
/** /**
@@ -99,13 +99,15 @@ export class RelationshipService extends DataService<Relationship> {
*/ */
getRelationshipEndpoint(uuid: string) { getRelationshipEndpoint(uuid: string) {
return this.getBrowseEndpoint().pipe( return this.getBrowseEndpoint().pipe(
map((href: string) => `${href}/${uuid}`) map((href: string) => `${href}/${uuid}`),
); );
} }
/** /**
* Send a delete request for a relationship by ID * Send a delete request for a relationship by ID
* @param id * @param id the ID of the relationship to delete
* @param copyVirtualMetadata whether to copy this relationship's virtual metadata to the related Items
* accepted values: none, all, left, right, configured
*/ */
deleteRelationship(id: string, copyVirtualMetadata: string): Observable<RemoteData<NoContent>> { deleteRelationship(id: string, copyVirtualMetadata: string): Observable<RemoteData<NoContent>> {
return this.getRelationshipEndpoint(id).pipe( return this.getRelationshipEndpoint(id).pipe(
@@ -469,7 +471,7 @@ export class RelationshipService extends DataService<Relationship> {
* @param object the {@link Relationship} to update * @param object the {@link Relationship} to update
*/ */
update(object: Relationship): Observable<RemoteData<Relationship>> { update(object: Relationship): Observable<RemoteData<Relationship>> {
return this.put(object); return this.putData.put(object);
} }
/** /**
@@ -513,7 +515,7 @@ export class RelationshipService extends DataService<Relationship> {
searchParams.push( searchParams.push(
{ {
fieldName: 'relatedItem', fieldName: 'relatedItem',
fieldValue: itemId fieldValue: itemId,
}, },
); );
}); });
@@ -521,8 +523,31 @@ export class RelationshipService extends DataService<Relationship> {
return this.searchBy( return this.searchBy(
'byItemsAndType', 'byItemsAndType',
{ {
searchParams: searchParams searchParams: searchParams,
}) as Observable<RemoteData<PaginatedList<Relationship>>>; },
) as Observable<RemoteData<PaginatedList<Relationship>>>;
} }
/**
* Make a new FindListRequest with given search method
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>}
* Return an observable that emits response from the server
*/
searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<Relationship>[]): Observable<RemoteData<PaginatedList<Relationship>>> {
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
getSearchByHref(searchMethod: string, options: FindListOptions, ...linksToFollow: FollowLinkConfig<Relationship>[]): Observable<string> {
return this.searchData.getSearchByHref(searchMethod, options, ...linksToFollow);
}
} }

View File

@@ -12,19 +12,20 @@ describe('RootDataService', () => {
let halService: HALEndpointService; let halService: HALEndpointService;
let restService; let restService;
let rootEndpoint; let rootEndpoint;
let findByHrefSpy;
beforeEach(() => { beforeEach(() => {
rootEndpoint = 'root-endpoint'; rootEndpoint = 'root-endpoint';
halService = jasmine.createSpyObj('halService', { halService = jasmine.createSpyObj('halService', {
getRootHref: rootEndpoint getRootHref: rootEndpoint,
}); });
restService = jasmine.createSpyObj('halService', { restService = jasmine.createSpyObj('halService', {
get: jasmine.createSpy('get') get: jasmine.createSpy('get'),
});
service = new RootDataService(null, null, null, null, halService, null, null, null, restService);
(service as any).dataService = jasmine.createSpyObj('dataService', {
findByHref: createSuccessfulRemoteDataObject$({})
}); });
service = new RootDataService(null, null, null, halService, restService);
findByHrefSpy = spyOn(service as any, 'findByHref');
findByHrefSpy.and.returnValue(createSuccessfulRemoteDataObject$({}));
}); });
describe('findRoot', () => { describe('findRoot', () => {
@@ -36,7 +37,7 @@ describe('RootDataService', () => {
it('should call findByHref using the root endpoint', (done) => { it('should call findByHref using the root endpoint', (done) => {
result$.subscribe(() => { result$.subscribe(() => {
expect((service as any).dataService.findByHref).toHaveBeenCalledWith(rootEndpoint, true, true); expect(findByHrefSpy).toHaveBeenCalledWith(rootEndpoint, true, true);
done(); done();
}); });
}); });

View File

@@ -1,70 +1,33 @@
/* eslint-disable max-classes-per-file */
import { DataService } from './data.service';
import { Root } from './root.model'; import { Root } from './root.model';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ROOT } from './root.resource-type'; import { ROOT } from './root.resource-type';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { Observable, of as observableOf } from 'rxjs'; import { Observable, of as observableOf } from 'rxjs';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { PaginatedList } from './paginated-list.model';
import { CoreState } from '../core-state.model';
import { FindListOptions } from './find-list-options.model';
import { DspaceRestService } from '../dspace-rest/dspace-rest.service'; import { DspaceRestService } from '../dspace-rest/dspace-rest.service';
import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model';
import { catchError, map } from 'rxjs/operators'; import { catchError, map } from 'rxjs/operators';
import { BaseDataService } from './base/base-data.service';
import { ObjectCacheService } from '../cache/object-cache.service';
/**
* A private DataService implementation to delegate specific methods to.
*/
class DataServiceImpl extends DataService<Root> {
protected linkPath = '';
protected responseMsToLive = 6 * 60 * 60 * 1000;
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: DefaultChangeAnalyzer<Root>) {
super();
}
}
/** /**
* A service to retrieve the {@link Root} object from the REST API. * A service to retrieve the {@link Root} object from the REST API.
*/ */
@Injectable() @Injectable()
@dataService(ROOT) @dataService(ROOT)
export class RootDataService { export class RootDataService extends BaseDataService<Root> {
/**
* A private DataService instance to delegate specific methods to.
*/
private dataService: DataServiceImpl;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, protected restService: DspaceRestService,
protected http: HttpClient, ) {
protected comparator: DefaultChangeAnalyzer<Root>, super('', requestService, rdbService, objectCache, halService, 6 * 60 * 60 * 1000);
protected restService: DspaceRestService) {
this.dataService = new DataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparator);
} }
/** /**
@@ -90,38 +53,7 @@ export class RootDataService {
* {@link HALLink}s should be automatically resolved * {@link HALLink}s should be automatically resolved
*/ */
findRoot(useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<Root>[]): Observable<RemoteData<Root>> { findRoot(useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<Root>[]): Observable<RemoteData<Root>> {
return this.dataService.findByHref(this.halService.getRootHref(), useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); return this.findByHref(this.halService.getRootHref(), useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Returns an observable of {@link RemoteData} of an object, based on an href, with a list of
* {@link FollowLinkConfig}, to automatically resolve {@link HALLink}s of the object
* @param href The url of object we want to retrieve
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
findByHref(href: string | Observable<string>, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<Root>[]): Observable<RemoteData<Root>> {
return this.dataService.findByHref(href, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Returns a list of observables of {@link RemoteData} of objects, based on an href, with a list
* of {@link FollowLinkConfig}, to automatically resolve {@link HALLink}s of the object
* @param href The url of object we want to retrieve
* @param findListOptions Find list options object
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
findAllByHref(href: string | Observable<string>, findListOptions: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<Root>[]): Observable<RemoteData<PaginatedList<Root>>> {
return this.dataService.findAllByHref(href, findListOptions, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
} }
/** /**

View File

@@ -4,14 +4,10 @@ import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { Site } from '../shared/site.model'; import { Site } from '../shared/site.model';
import { Store } from '@ngrx/store';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { TestScheduler } from 'rxjs/testing'; import { TestScheduler } from 'rxjs/testing';
import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
import { createPaginatedList } from '../../shared/testing/utils.test'; import { createPaginatedList } from '../../shared/testing/utils.test';
import { CoreState } from '../core-state.model';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
describe('SiteDataService', () => { describe('SiteDataService', () => {
@@ -47,21 +43,13 @@ describe('SiteDataService', () => {
}) })
}); });
const store = {} as Store<CoreState>;
objectCache = {} as ObjectCacheService; objectCache = {} as ObjectCacheService;
const notificationsService = {} as NotificationsService;
const http = {} as HttpClient;
const comparator = {} as any;
service = new SiteDataService( service = new SiteDataService(
requestService, requestService,
rdbService, rdbService,
store,
objectCache, objectCache,
halService, halService,
notificationsService,
http,
comparator
); );
}); });

View File

@@ -1,42 +1,38 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.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 { HALEndpointService } from '../shared/hal-endpoint.service';
import { getFirstSucceededRemoteData } from '../shared/operators'; import { getFirstSucceededRemoteData } from '../shared/operators';
import { Site } from '../shared/site.model'; import { Site } from '../shared/site.model';
import { SITE } from '../shared/site.resource-type'; import { SITE } from '../shared/site.resource-type';
import { DataService } from './data.service';
import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
import { PaginatedList } from './paginated-list.model'; import { PaginatedList } from './paginated-list.model';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { CoreState } from '../core-state.model'; import { BaseDataService } from './base/base-data.service';
import { FindAllData, FindAllDataImpl } from './base/find-all-data';
import { FollowLinkConfig } from 'src/app/shared/utils/follow-link-config.model';
import { FindListOptions } from './find-list-options.model';
import { ObjectCacheService } from '../cache/object-cache.service';
/** /**
* Service responsible for handling requests related to the Site object * Service responsible for handling requests related to the Site object
*/ */
@Injectable() @Injectable()
@dataService(SITE) @dataService(SITE)
export class SiteDataService extends DataService<Site> { export class SiteDataService extends BaseDataService<Site> implements FindAllData<Site> {
protected linkPath = 'sites'; private findAllData: FindAllData<Site>;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DSOChangeAnalyzer<Site>,
) { ) {
super(); super('sites', requestService, rdbService, objectCache, halService);
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
} }
/** /**
@@ -46,7 +42,25 @@ export class SiteDataService extends DataService<Site> {
return this.findAll().pipe( return this.findAll().pipe(
getFirstSucceededRemoteData(), getFirstSucceededRemoteData(),
map((remoteData: RemoteData<PaginatedList<Site>>) => remoteData.payload), map((remoteData: RemoteData<PaginatedList<Site>>) => remoteData.payload),
map((list: PaginatedList<Site>) => list.page[0]) map((list: PaginatedList<Site>) => list.page[0]),
); );
} }
/**
* 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 useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>>}
* Return an observable that emits object list
*/
public findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<Site>[]): Observable<RemoteData<PaginatedList<Site>>> {
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
} }

View File

@@ -1,18 +1,12 @@
import { HttpClient } from '@angular/common/http';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { TestScheduler } from 'rxjs/testing'; import { TestScheduler } from 'rxjs/testing';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { PageInfo } from '../shared/page-info.model';
import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
import { HrefOnlyDataService } from './href-only-data.service'; import { HrefOnlyDataService } from './href-only-data.service';
import { getMockHrefOnlyDataService } from '../../shared/mocks/href-only-data.service.mock'; import { getMockHrefOnlyDataService } from '../../shared/mocks/href-only-data.service.mock';
import { Store } from '@ngrx/store';
import { RestResponse } from '../cache/response.models'; import { RestResponse } from '../cache/response.models';
import { cold, getTestScheduler, hot } from 'jasmine-marbles'; import { cold, getTestScheduler, hot } from 'jasmine-marbles';
import { Item } from '../shared/item.model'; import { Item } from '../shared/item.model';
@@ -20,10 +14,8 @@ import { VersionDataService } from './version-data.service';
import { Version } from '../shared/version.model'; import { Version } from '../shared/version.model';
import { VersionHistory } from '../shared/version-history.model'; import { VersionHistory } from '../shared/version-history.model';
import { followLink } from '../../shared/utils/follow-link-config.model'; import { followLink } from '../../shared/utils/follow-link-config.model';
import { CoreState } from '../core-state.model';
import { RequestEntry } from './request-entry.model'; import { RequestEntry } from './request-entry.model';
describe('VersionDataService test', () => { describe('VersionDataService test', () => {
let scheduler: TestScheduler; let scheduler: TestScheduler;
let service: VersionDataService; let service: VersionDataService;
@@ -80,29 +72,17 @@ describe('VersionDataService test', () => {
const mockVersionRD = createSuccessfulRemoteDataObject(mockVersion); const mockVersionRD = createSuccessfulRemoteDataObject(mockVersion);
const endpointURL = `https://rest.api/rest/api/versioning/versions`; const endpointURL = `https://rest.api/rest/api/versioning/versions`;
const findByIdRequestURL = `https://rest.api/rest/api/versioning/versions/${mockVersion.id}`;
const findByIdRequestURL$ = observableOf(findByIdRequestURL);
const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a'; const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a';
objectCache = {} as ObjectCacheService; objectCache = {} as ObjectCacheService;
const notificationsService = {} as NotificationsService;
const http = {} as HttpClient;
const comparator = {} as any;
const comparatorEntry = {} as any; const comparatorEntry = {} as any;
const store = {} as Store<CoreState>;
const pageInfo = new PageInfo();
function initTestService() { function initTestService() {
hrefOnlyDataService = getMockHrefOnlyDataService(); hrefOnlyDataService = getMockHrefOnlyDataService();
return new VersionDataService( return new VersionDataService(
requestService, requestService,
rdbService, rdbService,
store,
objectCache, objectCache,
halService, halService,
notificationsService,
http,
comparatorEntry comparatorEntry
); );
} }
@@ -134,8 +114,7 @@ describe('VersionDataService test', () => {
service = initTestService(); service = initTestService();
spyOn((service as any), 'findByHref').and.callThrough(); spyOn((service as any), 'findById').and.callThrough();
spyOn((service as any), 'getIDHrefObs').and.returnValue(findByIdRequestURL$);
}); });
afterEach(() => { afterEach(() => {
@@ -147,7 +126,7 @@ describe('VersionDataService test', () => {
scheduler.schedule(() => service.getHistoryFromVersion(mockVersion, true, true)); scheduler.schedule(() => service.getHistoryFromVersion(mockVersion, true, true));
scheduler.flush(); scheduler.flush();
expect((service as any).findByHref).toHaveBeenCalledWith(findByIdRequestURL$, true, true, followLink('versionhistory')); expect((service as any).findById).toHaveBeenCalledWith(mockVersion.id, true, true, followLink('versionhistory'));
}); });
it('should return a VersionHistory', () => { it('should return a VersionHistory', () => {

View File

@@ -1,14 +1,9 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { DataService } from './data.service';
import { Version } from '../shared/version.model'; import { Version } from '../shared/version.model';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { EMPTY, Observable } from 'rxjs'; import { EMPTY, Observable } from 'rxjs';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { VERSION } from '../shared/version.resource-type'; import { VERSION } from '../shared/version.resource-type';
@@ -17,26 +12,30 @@ import { followLink } from '../../shared/utils/follow-link-config.model';
import { getFirstSucceededRemoteDataPayload } from '../shared/operators'; import { getFirstSucceededRemoteDataPayload } from '../shared/operators';
import { map, switchMap } from 'rxjs/operators'; import { map, switchMap } from 'rxjs/operators';
import { isNotEmpty } from '../../shared/empty.util'; import { isNotEmpty } from '../../shared/empty.util';
import { CoreState } from '../core-state.model'; import { RemoteData } from './remote-data';
import { PatchData, PatchDataImpl } from './base/patch-data';
import { RestRequestMethod } from './rest-request-method';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { IdentifiableDataService } from './base/identifiable-data.service';
/** /**
* Service responsible for handling requests related to the Version object * Service responsible for handling requests related to the Version object
*/ */
@Injectable() @Injectable()
@dataService(VERSION) @dataService(VERSION)
export class VersionDataService extends DataService<Version> { export class VersionDataService extends IdentifiableDataService<Version> implements PatchData<Version> {
protected linkPath = 'versions'; private patchData: PatchData<Version>;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, protected comparator: DefaultChangeAnalyzer<Version>,
protected http: HttpClient, ) {
protected comparator: DefaultChangeAnalyzer<Version>) { super('versions', requestService, rdbService, objectCache, halService);
super();
this.patchData = new PatchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, comparator, this.responseMsToLive, this.constructIdEndpoint);
} }
/** /**
@@ -65,4 +64,29 @@ export class VersionDataService extends DataService<Version> {
); );
} }
/**
* Send a patch request for a specified object
* @param {T} object The object to send a patch request for
* @param {Operation[]} operations The patch operations to be performed
*/
public patch(object: Version, operations: []): Observable<RemoteData<Version>> {
return this.patchData.patch(object, operations);
}
/**
* Add a new patch to the object cache
* The patch is derived from the differences between the given object and its version in the object cache
* @param {DSpaceObject} object The given object
*/
public update(object: Version): Observable<RemoteData<Version>> {
return this.patchData.update(object);
}
/**
* Commit current object changes to the server
* @param method The RestRequestMethod for which de server sync buffer should be committed
*/
public commitUpdates(method?: RestRequestMethod): void {
this.patchData.commitUpdates(method);
}
} }

View File

@@ -102,7 +102,7 @@ describe('VersionHistoryDataService', () => {
buildFromRequestUUID: jasmine.createSpy('buildFromRequestUUID'), buildFromRequestUUID: jasmine.createSpy('buildFromRequestUUID'),
}); });
objectCache = jasmine.createSpyObj('objectCache', { objectCache = jasmine.createSpyObj('objectCache', {
remove: jasmine.createSpy('remove') remove: jasmine.createSpy('remove'),
}); });
versionService = jasmine.createSpyObj('objectCache', { versionService = jasmine.createSpyObj('objectCache', {
findByHref: jasmine.createSpy('findByHref'), findByHref: jasmine.createSpy('findByHref'),
@@ -112,7 +112,13 @@ describe('VersionHistoryDataService', () => {
halService = new HALEndpointServiceStub(url); halService = new HALEndpointServiceStub(url);
notificationsService = new NotificationsServiceStub(); notificationsService = new NotificationsServiceStub();
service = new VersionHistoryDataService(requestService, rdbService, null, objectCache, halService, notificationsService, versionService, null, null); service = new VersionHistoryDataService(
requestService,
rdbService,
objectCache,
halService,
versionService,
);
} }
beforeEach(() => { beforeEach(() => {

View File

@@ -1,14 +1,10 @@
import { DataService } from './data.service';
import { VersionHistory } from '../shared/version-history.model'; import { VersionHistory } from '../shared/version-history.model';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpHeaders } from '@angular/common/http';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { PostRequest } from './request.models'; import { PostRequest } from './request.models';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model';
@@ -21,40 +17,31 @@ import { VERSION_HISTORY } from '../shared/version-history.resource-type';
import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { VersionDataService } from './version-data.service'; import { VersionDataService } from './version-data.service';
import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { HttpOptions } from '../dspace-rest/dspace-rest.service';
import { import { getAllSucceededRemoteData, getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload, getRemoteDataPayload } from '../shared/operators';
getAllSucceededRemoteData,
getFirstCompletedRemoteData,
getFirstSucceededRemoteDataPayload,
getRemoteDataPayload
} from '../shared/operators';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { hasValueOperator } from '../../shared/empty.util'; import { hasValueOperator } from '../../shared/empty.util';
import { Item } from '../shared/item.model'; import { Item } from '../shared/item.model';
import { CoreState } from '../core-state.model';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
import { sendRequest } from '../shared/request.operators'; import { sendRequest } from '../shared/request.operators';
import { RestRequest } from './rest-request.model'; import { RestRequest } from './rest-request.model';
import { IdentifiableDataService } from './base/identifiable-data.service';
/** /**
* Service responsible for handling requests related to the VersionHistory object * Service responsible for handling requests related to the VersionHistory object
*/ */
@Injectable() @Injectable()
@dataService(VERSION_HISTORY) @dataService(VERSION_HISTORY)
export class VersionHistoryDataService extends DataService<VersionHistory> { export class VersionHistoryDataService extends IdentifiableDataService<VersionHistory> {
protected linkPath = 'versionhistories';
protected versionsEndpoint = 'versions'; protected versionsEndpoint = 'versions';
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected versionDataService: VersionDataService, protected versionDataService: VersionDataService,
protected http: HttpClient, ) {
protected comparator: DefaultChangeAnalyzer<VersionHistory>) { super('versionhistories', requestService, rdbService, objectCache, halService);
super();
} }
/** /**

View File

@@ -1,41 +1,27 @@
import { DataService } from './data.service';
import { WorkflowAction } from '../tasks/models/workflow-action-object.model'; import { WorkflowAction } from '../tasks/models/workflow-action-object.model';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { Observable } from 'rxjs';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { WORKFLOW_ACTION } from '../tasks/models/workflow-action-object.resource-type'; import { WORKFLOW_ACTION } from '../tasks/models/workflow-action-object.resource-type';
import { CoreState } from '../core-state.model'; import { BaseDataService } from './base/base-data.service';
import { FindListOptions } from './find-list-options.model';
/** /**
* A service responsible for fetching/sending data from/to the REST API on the workflowactions endpoint * A service responsible for fetching/sending data from/to the REST API on the workflowactions endpoint
*/ */
@Injectable() @Injectable()
@dataService(WORKFLOW_ACTION) @dataService(WORKFLOW_ACTION)
export class WorkflowActionDataService extends DataService<WorkflowAction> { export class WorkflowActionDataService extends BaseDataService<WorkflowAction> {
protected linkPath = 'workflowactions'; protected linkPath = 'workflowactions';
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, ) {
protected http: HttpClient, super('workflowactions', requestService, rdbService, objectCache, halService);
protected comparator: DefaultChangeAnalyzer<WorkflowAction>) {
super();
}
getBrowseEndpoint(options: FindListOptions, linkPath?: string): Observable<string> {
return this.halService.getEndpoint(this.linkPath);
} }
} }

View File

@@ -13,7 +13,7 @@ import {
} from '../../access-control/epeople-registry/epeople-registry.actions'; } from '../../access-control/epeople-registry/epeople-registry.actions';
import { RequestParam } from '../cache/models/request-param.model'; import { RequestParam } from '../cache/models/request-param.model';
import { ChangeAnalyzer } from '../data/change-analyzer'; import { ChangeAnalyzer } from '../data/change-analyzer';
import { DeleteRequest, PatchRequest, PostRequest } from '../data/request.models'; import { PatchRequest, PostRequest } from '../data/request.models';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { Item } from '../shared/item.model'; import { Item } from '../shared/item.model';
@@ -47,12 +47,11 @@ describe('EPersonDataService', () => {
return new EPersonDataService( return new EPersonDataService(
requestService, requestService,
rdbService, rdbService,
store,
null, null,
halService, halService,
new DummyChangeAnalyzer() as any,
null, null,
null, store,
new DummyChangeAnalyzer() as any
); );
} }

View File

@@ -1,35 +1,36 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { createSelector, select, Store } from '@ngrx/store'; import { createSelector, select, Store } from '@ngrx/store';
import { Operation } from 'fast-json-patch'; import { Operation } from 'fast-json-patch';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { find, map, take } from 'rxjs/operators'; import { find, map, take } from 'rxjs/operators';
import { import { EPeopleRegistryCancelEPersonAction, EPeopleRegistryEditEPersonAction } from '../../access-control/epeople-registry/epeople-registry.actions';
EPeopleRegistryCancelEPersonAction,
EPeopleRegistryEditEPersonAction
} from '../../access-control/epeople-registry/epeople-registry.actions';
import { EPeopleRegistryState } from '../../access-control/epeople-registry/epeople-registry.reducers'; import { EPeopleRegistryState } from '../../access-control/epeople-registry/epeople-registry.reducers';
import { AppState } from '../../app.reducer'; import { AppState } from '../../app.reducer';
import { hasValue, hasNoValue } from '../../shared/empty.util'; import { hasNoValue, hasValue } from '../../shared/empty.util';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { RequestParam } from '../cache/models/request-param.model'; import { RequestParam } from '../cache/models/request-param.model';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { DataService } from '../data/data.service';
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
import { PaginatedList, buildPaginatedList } from '../data/paginated-list.model'; import { buildPaginatedList, PaginatedList } from '../data/paginated-list.model';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
import { PatchRequest, PostRequest, } from '../data/request.models'; import { PatchRequest, PostRequest } from '../data/request.models';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { getRemoteDataPayload, getFirstSucceededRemoteData, } from '../shared/operators'; import { getFirstSucceededRemoteData, getRemoteDataPayload } from '../shared/operators';
import { EPerson } from './models/eperson.model'; import { EPerson } from './models/eperson.model';
import { EPERSON } from './models/eperson.resource-type'; import { EPERSON } from './models/eperson.resource-type';
import { NoContent } from '../shared/NoContent.model'; import { NoContent } from '../shared/NoContent.model';
import { PageInfo } from '../shared/page-info.model'; import { PageInfo } from '../shared/page-info.model';
import { FindListOptions } from '../data/find-list-options.model'; import { FindListOptions } from '../data/find-list-options.model';
import { CreateData, CreateDataImpl } from '../data/base/create-data';
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
import { SearchData, SearchDataImpl } from '../data/base/search-data';
import { PatchData, PatchDataImpl } from '../data/base/patch-data';
import { DeleteData, DeleteDataImpl } from '../data/base/delete-data';
import { RestRequestMethod } from '../data/rest-request-method';
const ePeopleRegistryStateSelector = (state: AppState) => state.epeopleRegistry; const ePeopleRegistryStateSelector = (state: AppState) => state.epeopleRegistry;
const editEPersonSelector = createSelector(ePeopleRegistryStateSelector, (ePeopleRegistryState: EPeopleRegistryState) => ePeopleRegistryState.editEPerson); const editEPersonSelector = createSelector(ePeopleRegistryStateSelector, (ePeopleRegistryState: EPeopleRegistryState) => ePeopleRegistryState.editEPerson);
@@ -39,23 +40,30 @@ const editEPersonSelector = createSelector(ePeopleRegistryStateSelector, (ePeopl
*/ */
@Injectable() @Injectable()
@dataService(EPERSON) @dataService(EPERSON)
export class EPersonDataService extends DataService<EPerson> { export class EPersonDataService extends IdentifiableDataService<EPerson> implements CreateData<EPerson>, SearchData<EPerson>, PatchData<EPerson>, DeleteData<EPerson> {
protected linkPath = 'epersons';
protected searchByEmailPath = 'byEmail'; protected searchByEmailPath = 'byEmail';
protected searchByMetadataPath = 'byMetadata'; protected searchByMetadataPath = 'byMetadata';
private createData: CreateData<EPerson>;
private searchData: SearchData<EPerson>;
private patchData: PatchData<EPerson>;
private deleteData: DeleteData<EPerson>;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<any>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected comparator: DSOChangeAnalyzer<EPerson>,
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected http: HttpClient, protected store: Store<any>,
protected comparator: DSOChangeAnalyzer<EPerson>
) { ) {
super(); super('epersons', requestService, rdbService, objectCache, halService);
this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive);
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
this.patchData = new PatchDataImpl<EPerson>(this.linkPath, requestService, rdbService, objectCache, halService, comparator, this.responseMsToLive, this.constructIdEndpoint);
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
} }
/** /**
@@ -326,4 +334,90 @@ export class EPersonDataService extends DataService<EPerson> {
return this.rdbService.buildFromRequestUUID(requestId); return this.rdbService.buildFromRequestUUID(requestId);
} }
/**
* Create a new object on the server, and store the response in the object cache
*
* @param object The object to create
* @param params Array with additional params to combine with query string
*/
public create(object: EPerson, ...params: RequestParam[]): Observable<RemoteData<EPerson>> {
return this.createData.create(object, ...params);
}
/**
* Make a new FindListRequest with given search method
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>}
* Return an observable that emits response from the server
*/
searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<EPerson>[]): Observable<RemoteData<PaginatedList<EPerson>>> {
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Create the HREF for a specific object's search method with given options object
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @return {Observable<string>}
* Return an observable that emits created HREF
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
*/
getSearchByHref(searchMethod: string, options?: FindListOptions, ...linksToFollow: FollowLinkConfig<EPerson>[]): Observable<string> {
return this.searchData.getSearchByHref(searchMethod, options, ...linksToFollow);
}
/**
* Return a list of operations representing the difference between an object and its latest value in the cache.
* @param object the object to resolve to a list of patch operations
*/
public createPatchFromCache(object: EPerson): Observable<Operation[]> {
return this.patchData.createPatchFromCache(object);
}
patch(object: EPerson, operations: Operation[]): Observable<RemoteData<EPerson>> {
return this.patchData.patch(object, operations);
}
update(object: EPerson): Observable<RemoteData<EPerson>> {
return this.patchData.update(object);
}
commitUpdates(method?: RestRequestMethod): void {
this.patchData.commitUpdates(method);
}
/**
* Delete an existing object on the server
* @param objectId The id of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
*/
public delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.delete(objectId, copyVirtualMetadata);
}
/**
* Delete an existing object on the server
* @param href The self link of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
* Only emits once all request related to the DSO has been invalidated.
*/
public deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
}
} }

View File

@@ -65,15 +65,14 @@ describe('GroupDataService', () => {
function initTestService() { function initTestService() {
return new GroupDataService( return new GroupDataService(
requestService,
rdbService,
null,
halService,
new DummyChangeAnalyzer() as any, new DummyChangeAnalyzer() as any,
null, null,
null, null,
requestService,
rdbService,
store, store,
null,
halService,
null,
); );
} }

View File

@@ -1,4 +1,4 @@
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { createSelector, select, Store } from '@ngrx/store'; import { createSelector, select, Store } from '@ngrx/store';
@@ -15,7 +15,6 @@ import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { RequestParam } from '../cache/models/request-param.model'; import { RequestParam } from '../cache/models/request-param.model';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { DataService } from '../data/data.service';
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
import { PaginatedList } from '../data/paginated-list.model'; import { PaginatedList } from '../data/paginated-list.model';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
@@ -34,6 +33,13 @@ import { Community } from '../shared/community.model';
import { Collection } from '../shared/collection.model'; import { Collection } from '../shared/collection.model';
import { NoContent } from '../shared/NoContent.model'; import { NoContent } from '../shared/NoContent.model';
import { FindListOptions } from '../data/find-list-options.model'; import { FindListOptions } from '../data/find-list-options.model';
import { CreateData, CreateDataImpl } from '../data/base/create-data';
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
import { SearchData, SearchDataImpl } from '../data/base/search-data';
import { PatchData, PatchDataImpl } from '../data/base/patch-data';
import { DeleteData, DeleteDataImpl } from '../data/base/delete-data';
import { Operation } from 'fast-json-patch';
import { RestRequestMethod } from '../data/rest-request-method';
const groupRegistryStateSelector = (state: AppState) => state.groupRegistry; const groupRegistryStateSelector = (state: AppState) => state.groupRegistry;
const editGroupSelector = createSelector(groupRegistryStateSelector, (groupRegistryState: GroupRegistryState) => groupRegistryState.editGroup); const editGroupSelector = createSelector(groupRegistryStateSelector, (groupRegistryState: GroupRegistryState) => groupRegistryState.editGroup);
@@ -43,24 +49,32 @@ const editGroupSelector = createSelector(groupRegistryStateSelector, (groupRegis
*/ */
@Injectable() @Injectable()
@dataService(GROUP) @dataService(GROUP)
export class GroupDataService extends DataService<Group> { export class GroupDataService extends IdentifiableDataService<Group> {
protected linkPath = 'groups';
protected browseEndpoint = ''; protected browseEndpoint = '';
public ePersonsEndpoint = 'epersons'; public ePersonsEndpoint = 'epersons';
public subgroupsEndpoint = 'subgroups'; public subgroupsEndpoint = 'subgroups';
private createData: CreateData<Group>;
private searchData: SearchData<Group>;
private patchData: PatchData<Group>;
private deleteData: DeleteData<Group>;
constructor( constructor(
protected comparator: DSOChangeAnalyzer<Group>,
protected http: HttpClient,
protected notificationsService: NotificationsService,
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<any>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected comparator: DSOChangeAnalyzer<Group>,
protected notificationsService: NotificationsService,
protected nameService: DSONameService, protected nameService: DSONameService,
protected store: Store<any>,
) { ) {
super(); super('groups', requestService, rdbService, objectCache, halService);
this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive);
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
this.patchData = new PatchDataImpl<Group>(this.linkPath, requestService, rdbService, objectCache, halService, comparator, this.responseMsToLive, this.constructIdEndpoint);
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
} }
/** /**
@@ -322,4 +336,42 @@ export class GroupDataService extends DataService<Group> {
return responseRD$; return responseRD$;
} }
public create(object: Group, ...params: RequestParam[]): Observable<RemoteData<Group>> {
return this.createData.create(object, ...params);
}
//
searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<Group>[]): Observable<RemoteData<PaginatedList<Group>>> {
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
getSearchByHref(searchMethod: string, options?: FindListOptions, ...linksToFollow: FollowLinkConfig<Group>[]): Observable<string> {
return this.searchData.getSearchByHref(searchMethod, options, ...linksToFollow);
}
public createPatchFromCache(object: Group): Observable<Operation[]> {
return this.patchData.createPatchFromCache(object);
}
patch(object: Group, operations: Operation[]): Observable<RemoteData<Group>> {
return this.patchData.patch(object, operations);
}
update(object: Group): Observable<RemoteData<Group>> {
return this.patchData.update(object);
}
commitUpdates(method?: RestRequestMethod): void {
this.patchData.commitUpdates(method);
}
delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.delete(objectId, copyVirtualMetadata);
}
public deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
}
} }

View File

@@ -5,9 +5,7 @@ import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-servic
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { getMockRequestService } from '../../shared/mocks/request.service.mock';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
import { Feedback } from './models/feedback.model'; import { Feedback } from './models/feedback.model';
import { CoreState } from '../core-state.model'; import { CoreState } from '../core-state.model';
@@ -17,8 +15,6 @@ describe('FeedbackDataService', () => {
let halService; let halService;
let rdbService; let rdbService;
let notificationsService; let notificationsService;
let http;
let comparator;
let objectCache; let objectCache;
let store; let store;
let item; let item;
@@ -44,8 +40,6 @@ describe('FeedbackDataService', () => {
halService = new HALEndpointServiceStub('url') as any; halService = new HALEndpointServiceStub('url') as any;
rdbService = {} as RemoteDataBuildService; rdbService = {} as RemoteDataBuildService;
notificationsService = {} as NotificationsService; notificationsService = {} as NotificationsService;
http = {} as HttpClient;
comparator = new DSOChangeAnalyzer() as any;
objectCache = { objectCache = {
addPatch: () => { addPatch: () => {
@@ -63,8 +57,6 @@ describe('FeedbackDataService', () => {
objectCache, objectCache,
halService, halService,
notificationsService, notificationsService,
http,
comparator,
); );
} }

View File

@@ -1,6 +1,5 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { DataService } from '../data/data.service';
import { Feedback } from './models/feedback.model'; import { Feedback } from './models/feedback.model';
import { FEEDBACK } from './models/feedback.resource-type'; import { FEEDBACK } from './models/feedback.resource-type';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
@@ -10,17 +9,19 @@ import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
import { getFirstSucceededRemoteData, getRemoteDataPayload } from '../shared/operators'; import { getFirstSucceededRemoteData, getRemoteDataPayload } from '../shared/operators';
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
import { RemoteData } from '../data/remote-data';
import { RequestParam } from '../cache/models/request-param.model';
import { CreateData, CreateDataImpl } from '../data/base/create-data';
/** /**
* Service for checking and managing the feedback * Service for checking and managing the feedback
*/ */
@Injectable() @Injectable()
@dataService(FEEDBACK) @dataService(FEEDBACK)
export class FeedbackDataService extends DataService<Feedback> { export class FeedbackDataService extends IdentifiableDataService<Feedback> implements CreateData<Feedback> {
protected linkPath = 'feedbacks'; private createData: CreateDataImpl<Feedback>;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
@@ -29,10 +30,10 @@ export class FeedbackDataService extends DataService<Feedback> {
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DSOChangeAnalyzer<Feedback>,
) { ) {
super(); super('feedbacks', requestService, rdbService, objectCache, halService);
this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive);
} }
/** /**
@@ -46,4 +47,14 @@ export class FeedbackDataService extends DataService<Feedback> {
); );
} }
/**
* Create a new object on the server, and store the response in the object cache
*
* @param object The object to create
* @param params Array with additional params to combine with query string
*/
public create(object: Feedback, ...params: RequestParam[]): Observable<RemoteData<Feedback>> {
return this.createData.create(object, ...params);
}
} }

View File

@@ -165,7 +165,8 @@ describe('OrcidAuthService', () => {
routerStub = new RouterMock(); routerStub = new RouterMock();
researcherProfileService = jasmine.createSpyObj('ResearcherProfileService', { researcherProfileService = jasmine.createSpyObj('ResearcherProfileService', {
findById: jasmine.createSpy('findById'), findById: jasmine.createSpy('findById'),
updateByOrcidOperations: jasmine.createSpy('updateByOrcidOperations') patch: jasmine.createSpy('patch'),
updateByOrcidOperations: jasmine.createSpy('updateByOrcidOperations'),
}); });
configurationDataService = jasmine.createSpyObj('configurationDataService', { configurationDataService = jasmine.createSpyObj('configurationDataService', {
findByPropertyName: jasmine.createSpy('findByPropertyName') findByPropertyName: jasmine.createSpy('findByPropertyName')
@@ -237,7 +238,7 @@ describe('OrcidAuthService', () => {
describe('linkOrcidByItem', () => { describe('linkOrcidByItem', () => {
beforeEach(() => { beforeEach(() => {
scheduler = getTestScheduler(); scheduler = getTestScheduler();
researcherProfileService.updateByOrcidOperations.and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched)); researcherProfileService.patch.and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched));
researcherProfileService.findById.and.returnValue(createSuccessfulRemoteDataObject$(researcherProfile)); researcherProfileService.findById.and.returnValue(createSuccessfulRemoteDataObject$(researcherProfile));
}); });
@@ -251,14 +252,14 @@ describe('OrcidAuthService', () => {
scheduler.schedule(() => service.linkOrcidByItem(mockItemUnlinkedToOrcid, 'test-code').subscribe()); scheduler.schedule(() => service.linkOrcidByItem(mockItemUnlinkedToOrcid, 'test-code').subscribe());
scheduler.flush(); scheduler.flush();
expect(researcherProfileService.updateByOrcidOperations).toHaveBeenCalledWith(researcherProfile, operations); expect(researcherProfileService.patch).toHaveBeenCalledWith(researcherProfile, operations);
}); });
}); });
describe('unlinkOrcidByItem', () => { describe('unlinkOrcidByItem', () => {
beforeEach(() => { beforeEach(() => {
scheduler = getTestScheduler(); scheduler = getTestScheduler();
researcherProfileService.updateByOrcidOperations.and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched)); researcherProfileService.patch.and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched));
researcherProfileService.findById.and.returnValue(createSuccessfulRemoteDataObject$(researcherProfile)); researcherProfileService.findById.and.returnValue(createSuccessfulRemoteDataObject$(researcherProfile));
}); });
@@ -271,7 +272,7 @@ describe('OrcidAuthService', () => {
scheduler.schedule(() => service.unlinkOrcidByItem(mockItemLinkedToOrcid).subscribe()); scheduler.schedule(() => service.unlinkOrcidByItem(mockItemLinkedToOrcid).subscribe());
scheduler.flush(); scheduler.flush();
expect(researcherProfileService.updateByOrcidOperations).toHaveBeenCalledWith(researcherProfile, operations); expect(researcherProfileService.patch).toHaveBeenCalledWith(researcherProfile, operations);
}); });
}); });

View File

@@ -77,7 +77,7 @@ export class OrcidAuthService {
return this.researcherProfileService.findById(person.firstMetadata('dspace.object.owner').authority).pipe( return this.researcherProfileService.findById(person.firstMetadata('dspace.object.owner').authority).pipe(
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
switchMap((profileRD) => this.researcherProfileService.updateByOrcidOperations(profileRD.payload, operations)) switchMap((profileRD) => this.researcherProfileService.patch(profileRD.payload, operations)),
); );
} }
@@ -94,7 +94,7 @@ export class OrcidAuthService {
return this.researcherProfileService.findById(person.firstMetadata('dspace.object.owner').authority).pipe( return this.researcherProfileService.findById(person.firstMetadata('dspace.object.owner').authority).pipe(
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
switchMap((profileRD) => this.researcherProfileService.updateByOrcidOperations(profileRD.payload, operations)) switchMap((profileRD) => this.researcherProfileService.patch(profileRD.payload, operations)),
); );
} }

View File

@@ -1,16 +1,10 @@
// eslint-disable-next-line max-classes-per-file import { HttpHeaders } from '@angular/common/http';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, switchMap } from 'rxjs/operators'; import { map, switchMap } from 'rxjs/operators';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { DataService } from '../data/data.service';
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
import { ItemDataService } from '../data/item-data.service';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
import { PostRequest } from '../data/request.models'; import { PostRequest } from '../data/request.models';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
@@ -19,58 +13,24 @@ import { OrcidHistory } from './model/orcid-history.model';
import { ORCID_HISTORY } from './model/orcid-history.resource-type'; import { ORCID_HISTORY } from './model/orcid-history.resource-type';
import { OrcidQueue } from './model/orcid-queue.model'; import { OrcidQueue } from './model/orcid-queue.model';
import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { HttpOptions } from '../dspace-rest/dspace-rest.service';
import { CoreState } from '../core-state.model';
import { RestRequest } from '../data/rest-request.model'; import { RestRequest } from '../data/rest-request.model';
import { sendRequest } from '../shared/request.operators'; import { sendRequest } from '../shared/request.operators';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { IdentifiableDataService } from '../data/base/identifiable-data.service';
import { FindListOptions } from '../data/find-list-options.model';
import { PaginatedList } from '../data/paginated-list.model';
/**
* A private DataService implementation to delegate specific methods to.
*/
class OrcidHistoryServiceImpl extends DataService<OrcidHistory> {
public linkPath = 'orcidhistories';
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: DefaultChangeAnalyzer<OrcidHistory>) {
super();
}
}
/** /**
* A service that provides methods to make REST requests with Orcid History endpoint. * A service that provides methods to make REST requests with Orcid History endpoint.
*/ */
@Injectable() @Injectable()
@dataService(ORCID_HISTORY) @dataService(ORCID_HISTORY)
export class OrcidHistoryDataService { export class OrcidHistoryDataService extends IdentifiableDataService<OrcidHistory> {
dataService: OrcidHistoryServiceImpl;
responseMsToLive: number = 10 * 1000;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>, protected objectCache: ObjectCacheService,
protected objectCache: ObjectCacheService, protected halService: HALEndpointService,
protected halService: HALEndpointService, ) {
protected notificationsService: NotificationsService, super('orcidhistories', requestService, rdbService, objectCache, halService, 10 * 1000);
protected http: HttpClient,
protected comparator: DefaultChangeAnalyzer<OrcidHistory>,
protected itemService: ItemDataService ) {
this.dataService = new OrcidHistoryServiceImpl(requestService, rdbService, store, objectCache, halService,
notificationsService, http, comparator);
} }
sendToORCID(orcidQueue: OrcidQueue): Observable<RemoteData<OrcidHistory>> { sendToORCID(orcidQueue: OrcidQueue): Observable<RemoteData<OrcidHistory>> {
@@ -87,40 +47,4 @@ export class OrcidHistoryDataService {
switchMap((request: RestRequest) => this.rdbService.buildFromRequestUUID(request.uuid) as Observable<RemoteData<OrcidHistory>>) switchMap((request: RestRequest) => this.rdbService.buildFromRequestUUID(request.uuid) as Observable<RemoteData<OrcidHistory>>)
); );
} }
getEndpoint(): Observable<string> {
return this.halService.getEndpoint(this.dataService.linkPath);
}
/**
* Returns an observable of {@link RemoteData} of an object, based on its ID, with a list of
* {@link FollowLinkConfig}, to automatically resolve {@link HALLink}s of the object
* @param id ID of object we want to retrieve
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
findById(id: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<OrcidHistory>[]): Observable<RemoteData<OrcidHistory>> {
return this.dataService.findById(id, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Returns a list of observables of {@link RemoteData} of {@link OrcidHistory}s, based on an href, with a list of {@link FollowLinkConfig},
* to automatically resolve {@link HALLink}s of the {@link OrcidHistory}
* @param href The url of object we want to retrieve
* @param findListOptions Find list options object
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
findAllByHref(href: string, findListOptions: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<OrcidHistory>[]): Observable<RemoteData<PaginatedList<OrcidHistory>>> {
return this.dataService.findAllByHref(href, findListOptions, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
} }

View File

@@ -1,75 +1,42 @@
// eslint-disable-next-line max-classes-per-file
import { DataService } from '../data/data.service';
import { OrcidQueue } from './model/orcid-queue.model'; import { OrcidQueue } from './model/orcid-queue.model';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { ORCID_QUEUE } from './model/orcid-queue.resource-type'; import { ORCID_QUEUE } from './model/orcid-queue.resource-type';
import { ItemDataService } from '../data/item-data.service';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
import { PaginatedList } from '../data/paginated-list.model'; import { PaginatedList } from '../data/paginated-list.model';
import { RequestParam } from '../cache/models/request-param.model'; import { RequestParam } from '../cache/models/request-param.model';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { NoContent } from '../shared/NoContent.model'; import { NoContent } from '../shared/NoContent.model';
import { ConfigurationDataService } from '../data/configuration-data.service'; import { DeleteData, DeleteDataImpl } from '../data/base/delete-data';
import { Router } from '@angular/router'; import { SearchData, SearchDataImpl } from '../data/base/search-data';
import { CoreState } from '../core-state.model'; import { IdentifiableDataService } from '../data/base/identifiable-data.service';
/**
* A private DataService implementation to delegate specific methods to.
*/
class OrcidQueueServiceImpl extends DataService<OrcidQueue> {
public linkPath = 'orcidqueues';
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: DefaultChangeAnalyzer<OrcidQueue>) {
super();
}
}
/** /**
* A service that provides methods to make REST requests with Orcid Queue endpoint. * A service that provides methods to make REST requests with Orcid Queue endpoint.
*/ */
@Injectable() @Injectable()
@dataService(ORCID_QUEUE) @dataService(ORCID_QUEUE)
export class OrcidQueueService { export class OrcidQueueService extends IdentifiableDataService<OrcidQueue> {
private searchData: SearchData<OrcidQueue>;
dataService: OrcidQueueServiceImpl; private deleteData: DeleteData<OrcidQueue>;
responseMsToLive: number = 10 * 1000;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>, protected objectCache: ObjectCacheService,
protected objectCache: ObjectCacheService, protected halService: HALEndpointService,
protected halService: HALEndpointService, protected notificationsService: NotificationsService,
protected notificationsService: NotificationsService, ) {
protected http: HttpClient, super('orcidqueues', requestService, rdbService, objectCache, halService, 10 * 1000);
protected comparator: DefaultChangeAnalyzer<OrcidQueue>,
protected configurationService: ConfigurationDataService,
protected router: Router,
protected itemService: ItemDataService ) {
this.dataService = new OrcidQueueServiceImpl(requestService, rdbService, store, objectCache, halService,
notificationsService, http, comparator);
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
} }
/** /**
@@ -82,13 +49,13 @@ export class OrcidQueueService {
* @returns { OrcidQueue } * @returns { OrcidQueue }
*/ */
searchByProfileItemId(itemId: string, paginationOptions: PaginationComponentOptions, useCachedVersionIfAvailable = true, reRequestOnStale = true): Observable<RemoteData<PaginatedList<OrcidQueue>>> { searchByProfileItemId(itemId: string, paginationOptions: PaginationComponentOptions, useCachedVersionIfAvailable = true, reRequestOnStale = true): Observable<RemoteData<PaginatedList<OrcidQueue>>> {
return this.dataService.searchBy('findByProfileItem', { return this.searchData.searchBy('findByProfileItem', {
searchParams: [new RequestParam('profileItemId', itemId)], searchParams: [new RequestParam('profileItemId', itemId)],
elementsPerPage: paginationOptions.pageSize, elementsPerPage: paginationOptions.pageSize,
currentPage: paginationOptions.currentPage currentPage: paginationOptions.currentPage,
}, },
useCachedVersionIfAvailable, useCachedVersionIfAvailable,
reRequestOnStale reRequestOnStale,
); );
} }
@@ -97,14 +64,14 @@ export class OrcidQueueService {
* @returns { NoContent } * @returns { NoContent }
*/ */
deleteById(orcidQueueId: number): Observable<RemoteData<NoContent>> { deleteById(orcidQueueId: number): Observable<RemoteData<NoContent>> {
return this.dataService.delete(orcidQueueId.toString()); return this.deleteData.delete(orcidQueueId.toString());
} }
/** /**
* This method will set linkPath to stale * This method will set linkPath to stale
*/ */
clearFindByProfileItemRequests() { clearFindByProfileItemRequests() {
this.requestService.setStaleByHrefSubstring(this.dataService.linkPath + '/search/findByProfileItem'); this.requestService.setStaleByHrefSubstring(this.linkPath + '/search/findByProfileItem');
} }
} }

View File

@@ -253,46 +253,27 @@ describe('ResearcherProfileService', () => {
objectCache, objectCache,
halService, halService,
notificationsService, notificationsService,
http,
routerStub, routerStub,
comparator, comparator,
itemService itemService,
); );
serviceAsAny = service; serviceAsAny = service;
spyOn((service as any).dataService, 'create').and.callThrough(); spyOn((service as any), 'findById').and.callThrough();
spyOn((service as any).dataService, 'delete').and.callThrough(); spyOn((service as any), 'findByHref').and.callThrough();
spyOn((service as any).dataService, 'update').and.callThrough(); spyOn((service as any), 'getLinkPath').and.returnValue(observableOf(endpointURL));
spyOn((service as any).dataService, 'findById').and.callThrough(); spyOn((service as any).createData, 'create').and.callThrough();
spyOn((service as any).dataService, 'findByHref').and.callThrough(); spyOn((service as any).patchData, 'update').and.callThrough();
spyOn((service as any).dataService, 'searchBy').and.callThrough(); spyOn((service as any).searchData, 'searchBy').and.callThrough();
spyOn((service as any).dataService, 'getLinkPath').and.returnValue(observableOf(endpointURL)); spyOn((service as any).deleteData, 'delete').and.callThrough();
});
describe('findById', () => {
it('should proxy the call to dataservice.findById with eperson UUID', () => {
scheduler.schedule(() => service.findById(researcherProfileId));
scheduler.flush();
expect((service as any).dataService.findById).toHaveBeenCalledWith(researcherProfileId, true, true);
});
it('should return a ResearcherProfile object with the given id', () => {
const result = service.findById(researcherProfileId);
const expected = cold('a|', {
a: researcherProfileRD
});
expect(result).toBeObservable(expected);
});
}); });
describe('create', () => { describe('create', () => {
it('should proxy the call to dataservice.create with eperson UUID', () => { it('should proxy the call to createData.create with eperson UUID', () => {
scheduler.schedule(() => service.create()); scheduler.schedule(() => service.create());
scheduler.flush(); scheduler.flush();
expect((service as any).dataService.create).toHaveBeenCalled(); expect((service as any).createData.create).toHaveBeenCalled();
}); });
it('should return the RemoteData<ResearcherProfile> created', () => { it('should return the RemoteData<ResearcherProfile> created', () => {
@@ -305,11 +286,11 @@ describe('ResearcherProfileService', () => {
}); });
describe('delete', () => { describe('delete', () => {
it('should proxy the call to dataservice.delete', () => { it('should proxy the call to deleteData.delete', () => {
scheduler.schedule(() => service.delete(researcherProfile)); scheduler.schedule(() => service.delete(researcherProfile.id));
scheduler.flush(); scheduler.flush();
expect((service as any).dataService.delete).toHaveBeenCalledWith(researcherProfile.id); expect((service as any).deleteData.delete).toHaveBeenCalledWith(researcherProfile.id, undefined);
}); });
}); });
@@ -360,34 +341,33 @@ describe('ResearcherProfileService', () => {
}); });
describe('setVisibility', () => { describe('setVisibility', () => {
let patchSpy;
beforeEach(() => { beforeEach(() => {
spyOn((service as any).dataService, 'patch').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched)); spyOn((service as any).patchData, 'patch').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched));
spyOn((service as any), 'findById').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched)); (service.findById as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched));
}); });
it('should proxy the call to dataservice.patch', () => { it('should proxy the call to patchData.patch', () => {
const replaceOperation: ReplaceOperation<boolean> = { const replaceOperation: ReplaceOperation<boolean> = {
path: '/visible', path: '/visible',
op: 'replace', op: 'replace',
value: true value: true,
}; };
scheduler.schedule(() => service.setVisibility(researcherProfile, true)); scheduler.schedule(() => service.setVisibility(researcherProfile, true));
scheduler.flush(); scheduler.flush();
expect((service as any).dataService.patch).toHaveBeenCalledWith(researcherProfile, [replaceOperation]); expect((service as any).patchData.patch).toHaveBeenCalledWith(researcherProfile, [replaceOperation]);
}); });
}); });
describe('createFromExternalSource', () => { describe('createFromExternalSource', () => {
beforeEach(() => { beforeEach(() => {
spyOn((service as any).dataService, 'patch').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched)); spyOn((service as any).patchData, 'patch').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched));
spyOn((service as any), 'findById').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched)); (service.findById as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched));
}); });
it('should proxy the call to dataservice.patch', () => { it('should proxy the call to patchData.patch', () => {
const options: HttpOptions = Object.create({}); const options: HttpOptions = Object.create({});
let headers = new HttpHeaders(); let headers = new HttpHeaders();
headers = headers.append('Content-Type', 'text/uri-list'); headers = headers.append('Content-Type', 'text/uri-list');
@@ -406,14 +386,14 @@ describe('ResearcherProfileService', () => {
describe('updateByOrcidOperations', () => { describe('updateByOrcidOperations', () => {
beforeEach(() => { beforeEach(() => {
scheduler = getTestScheduler(); scheduler = getTestScheduler();
spyOn((service as any).dataService, 'patch').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched)); spyOn((service as any).patchData, 'patch').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched));
}); });
it('should call patch method properly', () => { it('should call patch method properly', () => {
scheduler.schedule(() => service.updateByOrcidOperations(researcherProfile, []).subscribe()); scheduler.schedule(() => service.patch(researcherProfile, []).subscribe());
scheduler.flush(); scheduler.flush();
expect((service as any).dataService.patch).toHaveBeenCalledWith(researcherProfile, []); expect((service as any).patchData.patch).toHaveBeenCalledWith(researcherProfile, []);
}); });
}); });
}); });

View File

@@ -1,9 +1,7 @@
/* eslint-disable max-classes-per-file */ import { HttpHeaders } from '@angular/common/http';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Store } from '@ngrx/store';
import { Operation, ReplaceOperation } from 'fast-json-patch'; import { Operation, ReplaceOperation } from 'fast-json-patch';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { find, map } from 'rxjs/operators'; import { find, map } from 'rxjs/operators';
@@ -11,54 +9,41 @@ import { NotificationsService } from '../../shared/notifications/notifications.s
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { DataService } from '../data/data.service';
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
import { ItemDataService } from '../data/item-data.service'; import { ItemDataService } from '../data/item-data.service';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NoContent } from '../shared/NoContent.model'; import { NoContent } from '../shared/NoContent.model';
import { getAllCompletedRemoteData, getFirstCompletedRemoteData } from '../shared/operators'; import { getFirstCompletedRemoteData } from '../shared/operators';
import { ResearcherProfile } from './model/researcher-profile.model'; import { ResearcherProfile } from './model/researcher-profile.model';
import { RESEARCHER_PROFILE } from './model/researcher-profile.resource-type'; import { RESEARCHER_PROFILE } from './model/researcher-profile.resource-type';
import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { HttpOptions } from '../dspace-rest/dspace-rest.service';
import { PostRequest } from '../data/request.models'; import { PostRequest } from '../data/request.models';
import { hasValue, isEmpty } from '../../shared/empty.util'; import { hasValue, isEmpty } from '../../shared/empty.util';
import { CoreState } from '../core-state.model';
import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { Item } from '../shared/item.model'; import { Item } from '../shared/item.model';
import { createFailedRemoteDataObject$ } from '../../shared/remote-data.utils'; import { createFailedRemoteDataObject$ } from '../../shared/remote-data.utils';
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
/** import { CreateData, CreateDataImpl } from '../data/base/create-data';
* A private DataService implementation to delegate specific methods to. import { SearchData, SearchDataImpl } from '../data/base/search-data';
*/ import { PatchData, PatchDataImpl } from '../data/base/patch-data';
class ResearcherProfileServiceImpl extends DataService<ResearcherProfile> { import { DeleteData, DeleteDataImpl } from '../data/base/delete-data';
protected linkPath = 'profiles'; import { RestRequestMethod } from '../data/rest-request-method';
import { RequestParam } from '../cache/models/request-param.model';
constructor( import { FindListOptions } from '../data/find-list-options.model';
protected requestService: RequestService, import { PaginatedList } from '../data/paginated-list.model';
protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DefaultChangeAnalyzer<ResearcherProfile>) {
super();
}
}
/** /**
* A service that provides methods to make REST requests with researcher profile endpoint. * A service that provides methods to make REST requests with researcher profile endpoint.
*/ */
@Injectable() @Injectable()
@dataService(RESEARCHER_PROFILE) @dataService(RESEARCHER_PROFILE)
export class ResearcherProfileService { export class ResearcherProfileService extends IdentifiableDataService<ResearcherProfile> implements CreateData<ResearcherProfile>, SearchData<ResearcherProfile>, PatchData<ResearcherProfile>, DeleteData<ResearcherProfile> {
private createData: CreateDataImpl<ResearcherProfile>;
protected dataService: ResearcherProfileServiceImpl; private searchData: SearchDataImpl<ResearcherProfile>;
private patchData: PatchDataImpl<ResearcherProfile>;
protected responseMsToLive: number = 10 * 1000; private deleteData: DeleteDataImpl<ResearcherProfile>;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
@@ -66,50 +51,16 @@ export class ResearcherProfileService {
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected http: HttpClient,
protected router: Router, protected router: Router,
protected comparator: DefaultChangeAnalyzer<ResearcherProfile>, protected comparator: DefaultChangeAnalyzer<ResearcherProfile>,
protected itemService: ItemDataService) { protected itemService: ItemDataService,
) {
super('profiles', requestService, rdbService, objectCache, halService, 10 * 1000);
this.dataService = new ResearcherProfileServiceImpl(requestService, rdbService, null, objectCache, halService, this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive);
notificationsService, http, comparator); this.patchData = new PatchDataImpl<ResearcherProfile>(this.linkPath, requestService, rdbService, objectCache, halService, comparator, this.responseMsToLive, this.constructIdEndpoint);
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
} this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
/**
* Find the researcher profile with the given uuid.
*
* @param uuid the profile uuid
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
public findById(uuid: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<ResearcherProfile>[]): Observable<RemoteData<ResearcherProfile>> {
return this.dataService.findById(uuid, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow).pipe(
getAllCompletedRemoteData(),
);
}
/**
* Create a new researcher profile for the current user.
*/
public create(): Observable<RemoteData<ResearcherProfile>> {
return this.dataService.create(new ResearcherProfile());
}
/**
* Delete a researcher profile.
*
* @param researcherProfile the profile to delete
*/
public delete(researcherProfile: ResearcherProfile): Observable<boolean> {
return this.dataService.delete(researcherProfile.id).pipe(
getFirstCompletedRemoteData(),
map((response: RemoteData<NoContent>) => response.isSuccess)
);
} }
/** /**
@@ -152,7 +103,7 @@ export class ResearcherProfileService {
value: visible value: visible
}; };
return this.dataService.patch(researcherProfile, [replaceOperation]); return this.patch(researcherProfile, [replaceOperation]);
} }
/** /**
@@ -166,11 +117,11 @@ export class ResearcherProfileService {
options.headers = headers; options.headers = headers;
const requestId = this.requestService.generateRequestId(); const requestId = this.requestService.generateRequestId();
const href$ = this.halService.getEndpoint(this.dataService.getLinkPath()); const href$ = this.halService.getEndpoint(this.getLinkPath());
href$.pipe( href$.pipe(
find((href: string) => hasValue(href)), find((href: string) => hasValue(href)),
map((href: string) => this.dataService.buildHrefWithParams(href, [], followLink('item'))) map((href: string) => this.buildHrefWithParams(href, [], followLink('item'))),
).subscribe((endpoint: string) => { ).subscribe((endpoint: string) => {
const request = new PostRequest(requestId, endpoint, sourceUri, options); const request = new PostRequest(requestId, endpoint, sourceUri, options);
this.requestService.send(request); this.requestService.send(request);
@@ -179,16 +130,85 @@ export class ResearcherProfileService {
return this.rdbService.buildFromRequestUUID(requestId, followLink('item')); return this.rdbService.buildFromRequestUUID(requestId, followLink('item'));
} }
/** /**
* Update researcher profile by patch orcid operation * Create a new object on the server, and store the response in the object cache
* *
* @param researcherProfile * @param object The object to create
* @param operations * @param params Array with additional params to combine with query string
*/ */
public updateByOrcidOperations(researcherProfile: ResearcherProfile, operations: Operation[]): Observable<RemoteData<ResearcherProfile>> { public create(object?: ResearcherProfile, ...params: RequestParam[]): Observable<RemoteData<ResearcherProfile>> {
return this.dataService.patch(researcherProfile, operations); if (isEmpty(object)) {
object = new ResearcherProfile();
}
return this.createData.create(object, ...params);
} }
searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<ResearcherProfile>[]): Observable<RemoteData<PaginatedList<ResearcherProfile>>> {
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
getSearchByHref?(searchMethod: string, options?: FindListOptions, ...linksToFollow: FollowLinkConfig<ResearcherProfile>[]): Observable<string> {
return this.searchData.getSearchByHref(searchMethod, options, ...linksToFollow);
}
/**
* Commit current object changes to the server
* @param method The RestRequestMethod for which de server sync buffer should be committed
*/
public commitUpdates(method?: RestRequestMethod): void {
this.patchData.commitUpdates(method);
}
/**
* Return a list of operations representing the difference between an object and its latest value in the cache.
* @param object the object to resolve to a list of patch operations
*/
public createPatchFromCache(object: ResearcherProfile): Observable<Operation[]> {
return this.patchData.createPatchFromCache(object);
}
/**
* Send a patch request for a specified object
* @param {T} object The object to send a patch request for
* @param {Operation[]} operations The patch operations to be performed
*/
public patch(object: ResearcherProfile, operations: Operation[]): Observable<RemoteData<ResearcherProfile>> {
return this.patchData.patch(object, operations);
}
/**
* Add a new patch to the object cache
* The patch is derived from the differences between the given object and its version in the object cache
* @param {DSpaceObject} object The given object
*/
public update(object: ResearcherProfile): Observable<RemoteData<ResearcherProfile>> {
return this.patchData.update(object);
}
/**
* Delete an existing object on the server
* @param objectId The id of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
*/
public delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.delete(objectId, copyVirtualMetadata);
}
/**
* Delete an existing object on the server
* @param href The self link of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
* Only emits once all request related to the DSO has been invalidated.
*/
public deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
}
} }

View File

@@ -131,7 +131,6 @@ describe('ResourcePolicyService', () => {
}); });
objectCache = {} as ObjectCacheService; objectCache = {} as ObjectCacheService;
const notificationsService = {} as NotificationsService; const notificationsService = {} as NotificationsService;
const http = {} as HttpClient;
const comparator = {} as any; const comparator = {} as any;
service = new ResourcePolicyService( service = new ResourcePolicyService(
@@ -140,42 +139,41 @@ describe('ResourcePolicyService', () => {
objectCache, objectCache,
halService, halService,
notificationsService, notificationsService,
http,
comparator, comparator,
ePersonService, ePersonService,
groupService groupService,
); );
spyOn((service as any).dataService, 'create').and.callThrough(); spyOn((service as any).createData, 'create').and.callThrough();
spyOn((service as any).dataService, 'delete').and.callThrough(); spyOn((service as any).deleteData, 'delete').and.callThrough();
spyOn((service as any).dataService, 'update').and.callThrough(); spyOn((service as any).patchData, 'update').and.callThrough();
spyOn((service as any).dataService, 'findById').and.callThrough(); spyOn((service as any), 'findById').and.callThrough();
spyOn((service as any).dataService, 'findByHref').and.callThrough(); spyOn((service as any), 'findByHref').and.callThrough();
spyOn((service as any).dataService, 'searchBy').and.callThrough(); spyOn((service as any).searchData, 'searchBy').and.callThrough();
spyOn((service as any).dataService, 'getSearchByHref').and.returnValue(observableOf(requestURL)); spyOn((service as any).searchData, 'getSearchByHref').and.returnValue(observableOf(requestURL));
}); });
describe('create', () => { describe('create', () => {
it('should proxy the call to dataservice.create with eperson UUID', () => { it('should proxy the call to createData.create with eperson UUID', () => {
scheduler.schedule(() => service.create(resourcePolicy, resourceUUID, epersonUUID)); scheduler.schedule(() => service.create(resourcePolicy, resourceUUID, epersonUUID));
const params = [ const params = [
new RequestParam('resource', resourceUUID), new RequestParam('resource', resourceUUID),
new RequestParam('eperson', epersonUUID) new RequestParam('eperson', epersonUUID),
]; ];
scheduler.flush(); scheduler.flush();
expect((service as any).dataService.create).toHaveBeenCalledWith(resourcePolicy, ...params); expect((service as any).createData.create).toHaveBeenCalledWith(resourcePolicy, ...params);
}); });
it('should proxy the call to dataservice.create with group UUID', () => { it('should proxy the call to createData.create with group UUID', () => {
scheduler.schedule(() => service.create(resourcePolicy, resourceUUID, null, groupUUID)); scheduler.schedule(() => service.create(resourcePolicy, resourceUUID, null, groupUUID));
const params = [ const params = [
new RequestParam('resource', resourceUUID), new RequestParam('resource', resourceUUID),
new RequestParam('group', groupUUID) new RequestParam('group', groupUUID),
]; ];
scheduler.flush(); scheduler.flush();
expect((service as any).dataService.create).toHaveBeenCalledWith(resourcePolicy, ...params); expect((service as any).createData.create).toHaveBeenCalledWith(resourcePolicy, ...params);
}); });
it('should return a RemoteData<ResourcePolicy> for the object with the given id', () => { it('should return a RemoteData<ResourcePolicy> for the object with the given id', () => {
@@ -188,31 +186,24 @@ describe('ResourcePolicyService', () => {
}); });
describe('delete', () => { describe('delete', () => {
it('should proxy the call to dataservice.create', () => { it('should proxy the call to deleteData.delete', () => {
scheduler.schedule(() => service.delete(resourcePolicyId)); scheduler.schedule(() => service.delete(resourcePolicyId));
scheduler.flush(); scheduler.flush();
expect((service as any).dataService.delete).toHaveBeenCalledWith(resourcePolicyId); expect((service as any).deleteData.delete).toHaveBeenCalledWith(resourcePolicyId);
}); });
}); });
describe('update', () => { describe('update', () => {
it('should proxy the call to dataservice.update', () => { it('should proxy the call to updateData.update', () => {
scheduler.schedule(() => service.update(resourcePolicy)); scheduler.schedule(() => service.update(resourcePolicy));
scheduler.flush(); scheduler.flush();
expect((service as any).dataService.update).toHaveBeenCalledWith(resourcePolicy); expect((service as any).patchData.update).toHaveBeenCalledWith(resourcePolicy);
}); });
}); });
describe('findById', () => { describe('findById', () => {
it('should proxy the call to dataservice.findById', () => {
scheduler.schedule(() => service.findById(resourcePolicyId));
scheduler.flush();
expect((service as any).dataService.findById).toHaveBeenCalledWith(resourcePolicyId, true, true);
});
it('should return a RemoteData<ResourcePolicy> for the object with the given id', () => { it('should return a RemoteData<ResourcePolicy> for the object with the given id', () => {
const result = service.findById(resourcePolicyId); const result = service.findById(resourcePolicyId);
const expected = cold('a|', { const expected = cold('a|', {
@@ -223,13 +214,6 @@ describe('ResourcePolicyService', () => {
}); });
describe('findByHref', () => { 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, true, true);
});
it('should return a RemoteData<ResourcePolicy> for the object with the given URL', () => { it('should return a RemoteData<ResourcePolicy> for the object with the given URL', () => {
const result = service.findByHref(requestURL); const result = service.findByHref(requestURL);
const expected = cold('a|', { const expected = cold('a|', {
@@ -240,16 +224,16 @@ describe('ResourcePolicyService', () => {
}); });
describe('searchByEPerson', () => { describe('searchByEPerson', () => {
it('should proxy the call to dataservice.searchBy', () => { it('should proxy the call to searchData.searchBy', () => {
const options = new FindListOptions(); const options = new FindListOptions();
options.searchParams = [new RequestParam('uuid', epersonUUID)]; options.searchParams = [new RequestParam('uuid', epersonUUID)];
scheduler.schedule(() => service.searchByEPerson(epersonUUID)); scheduler.schedule(() => service.searchByEPerson(epersonUUID));
scheduler.flush(); scheduler.flush();
expect((service as any).dataService.searchBy).toHaveBeenCalledWith((service as any).searchByEPersonMethod, options, true, true); expect((service as any).searchData.searchBy).toHaveBeenCalledWith((service as any).searchByEPersonMethod, options, true, true);
}); });
it('should proxy the call to dataservice.searchBy with additional search param', () => { it('should proxy the call to searchData.searchBy with additional search param', () => {
const options = new FindListOptions(); const options = new FindListOptions();
options.searchParams = [ options.searchParams = [
new RequestParam('uuid', epersonUUID), new RequestParam('uuid', epersonUUID),
@@ -258,7 +242,7 @@ describe('ResourcePolicyService', () => {
scheduler.schedule(() => service.searchByEPerson(epersonUUID, resourceUUID)); scheduler.schedule(() => service.searchByEPerson(epersonUUID, resourceUUID));
scheduler.flush(); scheduler.flush();
expect((service as any).dataService.searchBy).toHaveBeenCalledWith((service as any).searchByEPersonMethod, options, true, true); expect((service as any).searchData.searchBy).toHaveBeenCalledWith((service as any).searchByEPersonMethod, options, true, true);
}); });
it('should return a RemoteData<PaginatedList<ResourcePolicy>) for the search', () => { it('should return a RemoteData<PaginatedList<ResourcePolicy>) for the search', () => {
@@ -272,16 +256,16 @@ describe('ResourcePolicyService', () => {
}); });
describe('searchByGroup', () => { describe('searchByGroup', () => {
it('should proxy the call to dataservice.searchBy', () => { it('should proxy the call to searchData.searchBy', () => {
const options = new FindListOptions(); const options = new FindListOptions();
options.searchParams = [new RequestParam('uuid', groupUUID)]; options.searchParams = [new RequestParam('uuid', groupUUID)];
scheduler.schedule(() => service.searchByGroup(groupUUID)); scheduler.schedule(() => service.searchByGroup(groupUUID));
scheduler.flush(); scheduler.flush();
expect((service as any).dataService.searchBy).toHaveBeenCalledWith((service as any).searchByGroupMethod, options, true, true); expect((service as any).searchData.searchBy).toHaveBeenCalledWith((service as any).searchByGroupMethod, options, true, true);
}); });
it('should proxy the call to dataservice.searchBy with additional search param', () => { it('should proxy the call to searchData.searchBy with additional search param', () => {
const options = new FindListOptions(); const options = new FindListOptions();
options.searchParams = [ options.searchParams = [
new RequestParam('uuid', groupUUID), new RequestParam('uuid', groupUUID),
@@ -290,7 +274,7 @@ describe('ResourcePolicyService', () => {
scheduler.schedule(() => service.searchByGroup(groupUUID, resourceUUID)); scheduler.schedule(() => service.searchByGroup(groupUUID, resourceUUID));
scheduler.flush(); scheduler.flush();
expect((service as any).dataService.searchBy).toHaveBeenCalledWith((service as any).searchByGroupMethod, options, true, true); expect((service as any).searchData.searchBy).toHaveBeenCalledWith((service as any).searchByGroupMethod, options, true, true);
}); });
it('should return a RemoteData<PaginatedList<ResourcePolicy>) for the search', () => { it('should return a RemoteData<PaginatedList<ResourcePolicy>) for the search', () => {
@@ -304,16 +288,16 @@ describe('ResourcePolicyService', () => {
}); });
describe('searchByResource', () => { describe('searchByResource', () => {
it('should proxy the call to dataservice.searchBy', () => { it('should proxy the call to searchData.searchBy', () => {
const options = new FindListOptions(); const options = new FindListOptions();
options.searchParams = [new RequestParam('uuid', resourceUUID)]; options.searchParams = [new RequestParam('uuid', resourceUUID)];
scheduler.schedule(() => service.searchByResource(resourceUUID)); scheduler.schedule(() => service.searchByResource(resourceUUID));
scheduler.flush(); scheduler.flush();
expect((service as any).dataService.searchBy).toHaveBeenCalledWith((service as any).searchByResourceMethod, options, true, true); expect((service as any).searchData.searchBy).toHaveBeenCalledWith((service as any).searchByResourceMethod, options, true, true);
}); });
it('should proxy the call to dataservice.searchBy with additional search param', () => { it('should proxy the call to searchData.searchBy with additional search param', () => {
const action = ActionType.READ; const action = ActionType.READ;
const options = new FindListOptions(); const options = new FindListOptions();
options.searchParams = [ options.searchParams = [
@@ -323,7 +307,7 @@ describe('ResourcePolicyService', () => {
scheduler.schedule(() => service.searchByResource(resourceUUID, action)); scheduler.schedule(() => service.searchByResource(resourceUUID, action));
scheduler.flush(); scheduler.flush();
expect((service as any).dataService.searchBy).toHaveBeenCalledWith((service as any).searchByResourceMethod, options, true, true); expect((service as any).searchData.searchBy).toHaveBeenCalledWith((service as any).searchByResourceMethod, options, true, true);
}); });
it('should return a RemoteData<PaginatedList<ResourcePolicy>) for the search', () => { it('should return a RemoteData<PaginatedList<ResourcePolicy>) for the search', () => {

View File

@@ -1,13 +1,8 @@
/* eslint-disable max-classes-per-file */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { HttpHeaders } from '@angular/common/http';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { DataService } from '../data/data.service';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { Collection } from '../shared/collection.model'; import { Collection } from '../shared/collection.model';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
@@ -17,7 +12,6 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { RESOURCE_POLICY } from './models/resource-policy.resource-type'; import { RESOURCE_POLICY } from './models/resource-policy.resource-type';
import { ChangeAnalyzer } from '../data/change-analyzer';
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
import { PaginatedList } from '../data/paginated-list.model'; import { PaginatedList } from '../data/paginated-list.model';
import { ActionType } from './models/action-type.model'; import { ActionType } from './models/action-type.model';
@@ -26,7 +20,6 @@ import { isNotEmpty } from '../../shared/empty.util';
import { map, take } from 'rxjs/operators'; import { map, take } from 'rxjs/operators';
import { NoContent } from '../shared/NoContent.model'; import { NoContent } from '../shared/NoContent.model';
import { getFirstCompletedRemoteData } from '../shared/operators'; import { getFirstCompletedRemoteData } from '../shared/operators';
import { CoreState } from '../core-state.model';
import { FindListOptions } from '../data/find-list-options.model'; import { FindListOptions } from '../data/find-list-options.model';
import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { HttpOptions } from '../dspace-rest/dspace-rest.service';
import { PutRequest } from '../data/request.models'; import { PutRequest } from '../data/request.models';
@@ -36,52 +29,43 @@ import { StatusCodeOnlyResponseParsingService } from '../data/status-code-only-r
import { HALLink } from '../shared/hal-link.model'; import { HALLink } from '../shared/hal-link.model';
import { EPersonDataService } from '../eperson/eperson-data.service'; import { EPersonDataService } from '../eperson/eperson-data.service';
import { GroupDataService } from '../eperson/group-data.service'; import { GroupDataService } from '../eperson/group-data.service';
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
import { CreateDataImpl } from '../data/base/create-data';
/** import { SearchDataImpl } from '../data/base/search-data';
* A private DataService implementation to delegate specific methods to. import { PatchDataImpl } from '../data/base/patch-data';
*/ import { DeleteDataImpl } from '../data/base/delete-data';
class DataServiceImpl extends DataService<ResourcePolicy> {
protected linkPath = 'resourcepolicies';
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<ResourcePolicy>,
) {
super();
}
}
/** /**
* A service responsible for fetching/sending data from/to the REST API on the resourcepolicies endpoint * A service responsible for fetching/sending data from/to the REST API on the resourcepolicies endpoint
*/ */
@Injectable() @Injectable()
@dataService(RESOURCE_POLICY) @dataService(RESOURCE_POLICY)
export class ResourcePolicyService { export class ResourcePolicyService extends IdentifiableDataService<ResourcePolicy> {
private dataService: DataServiceImpl;
protected searchByEPersonMethod = 'eperson'; protected searchByEPersonMethod = 'eperson';
protected searchByGroupMethod = 'group'; protected searchByGroupMethod = 'group';
protected searchByResourceMethod = 'resource'; protected searchByResourceMethod = 'resource';
private createData: CreateDataImpl<ResourcePolicy>;
private searchData: SearchDataImpl<ResourcePolicy>;
private patchData: PatchDataImpl<ResourcePolicy>;
private deleteData: DeleteDataImpl<ResourcePolicy>;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DefaultChangeAnalyzer<ResourcePolicy>, protected comparator: DefaultChangeAnalyzer<ResourcePolicy>,
protected ePersonService: EPersonDataService, protected ePersonService: EPersonDataService,
protected groupService: GroupDataService, protected groupService: GroupDataService,
) { ) {
this.dataService = new DataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparator); super('resourcepolicies', requestService, rdbService, objectCache, halService);
this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive);
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
this.patchData = new PatchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, comparator, this.responseMsToLive, this.constructIdEndpoint);
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
} }
/** /**
@@ -105,7 +89,7 @@ export class ResourcePolicyService {
} else if (isNotEmpty(groupUUID)) { } else if (isNotEmpty(groupUUID)) {
params.push(new RequestParam('group', groupUUID)); params.push(new RequestParam('group', groupUUID));
} }
return this.dataService.create(resourcePolicy, ...params); return this.createData.create(resourcePolicy, ...params);
} }
/** /**
@@ -115,9 +99,9 @@ export class ResourcePolicyService {
* @return an observable that emits true when the deletion was successful, false when it failed * @return an observable that emits true when the deletion was successful, false when it failed
*/ */
delete(resourcePolicyID: string): Observable<boolean> { delete(resourcePolicyID: string): Observable<boolean> {
return this.dataService.delete(resourcePolicyID).pipe( return this.deleteData.delete(resourcePolicyID).pipe(
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
map((response: RemoteData<NoContent>) => response.hasSucceeded) map((response: RemoteData<NoContent>) => response.hasSucceeded),
); );
} }
@@ -127,37 +111,7 @@ export class ResourcePolicyService {
* @param {ResourcePolicy} object The given object * @param {ResourcePolicy} object The given object
*/ */
update(object: ResourcePolicy): Observable<RemoteData<ResourcePolicy>> { update(object: ResourcePolicy): Observable<RemoteData<ResourcePolicy>> {
return this.dataService.update(object); return this.patchData.update(object);
}
/**
* Returns an observable of {@link RemoteData} of a {@link ResourcePolicy}, based on an href, with a list of {@link FollowLinkConfig},
* to automatically resolve {@link HALLink}s of the {@link ResourcePolicy}
* @param href The url of object we want to retrieve
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
findByHref(href: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<ResourcePolicy>[]): Observable<RemoteData<ResourcePolicy>> {
return this.dataService.findByHref(href, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Returns an observable of {@link RemoteData} of a {@link ResourcePolicy}, based on its ID, with a list of {@link FollowLinkConfig},
* to automatically resolve {@link HALLink}s of the object
* @param id ID of {@link ResourcePolicy} we want to retrieve
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
findById(id: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<ResourcePolicy>[]): Observable<RemoteData<ResourcePolicy>> {
return this.dataService.findById(id, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
} }
/** /**
@@ -167,7 +121,7 @@ export class ResourcePolicyService {
* @param findListOptions the {@link FindListOptions} for the request * @param findListOptions the {@link FindListOptions} for the request
*/ */
getDefaultAccessConditionsFor(collection: Collection, findListOptions?: FindListOptions): Observable<RemoteData<PaginatedList<ResourcePolicy>>> { getDefaultAccessConditionsFor(collection: Collection, findListOptions?: FindListOptions): Observable<RemoteData<PaginatedList<ResourcePolicy>>> {
return this.dataService.findAllByHref(collection._links.defaultAccessConditions.href, findListOptions); return this.findAllByHref(collection._links.defaultAccessConditions.href, findListOptions);
} }
/** /**
@@ -188,7 +142,7 @@ export class ResourcePolicyService {
if (isNotEmpty(resourceUUID)) { if (isNotEmpty(resourceUUID)) {
options.searchParams.push(new RequestParam('resource', resourceUUID)); options.searchParams.push(new RequestParam('resource', resourceUUID));
} }
return this.dataService.searchBy(this.searchByEPersonMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); return this.searchData.searchBy(this.searchByEPersonMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
} }
/** /**
@@ -209,7 +163,7 @@ export class ResourcePolicyService {
if (isNotEmpty(resourceUUID)) { if (isNotEmpty(resourceUUID)) {
options.searchParams.push(new RequestParam('resource', resourceUUID)); options.searchParams.push(new RequestParam('resource', resourceUUID));
} }
return this.dataService.searchBy(this.searchByGroupMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); return this.searchData.searchBy(this.searchByGroupMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
} }
/** /**
@@ -230,7 +184,7 @@ export class ResourcePolicyService {
if (isNotEmpty(action)) { if (isNotEmpty(action)) {
options.searchParams.push(new RequestParam('action', action)); options.searchParams.push(new RequestParam('action', action));
} }
return this.dataService.searchBy(this.searchByResourceMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); return this.searchData.searchBy(this.searchByResourceMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
} }
/** /**
@@ -256,7 +210,7 @@ export class ResourcePolicyService {
const requestId = this.requestService.generateRequestId(); const requestId = this.requestService.generateRequestId();
this.requestService.setStaleByHrefSubstring(`${this.dataService.getLinkPath()}/${resourcePolicyId}/${targetType}`); this.requestService.setStaleByHrefSubstring(`${this.getLinkPath()}/${resourcePolicyId}/${targetType}`);
targetEndpoint$.subscribe((targetEndpoint) => { targetEndpoint$.subscribe((targetEndpoint) => {
const resourceEndpoint = resourcePolicyHref + '/' + targetType; const resourceEndpoint = resourcePolicyHref + '/' + targetType;

View File

@@ -1,14 +1,12 @@
/* eslint-disable max-classes-per-file */ /* eslint-disable max-classes-per-file */
import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
import { Injectable, OnDestroy } from '@angular/core'; import { Injectable, OnDestroy } from '@angular/core';
import { Router } from '@angular/router';
import { map, switchMap, take } from 'rxjs/operators'; import { map, switchMap, take } from 'rxjs/operators';
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
import { LinkService } from '../../cache/builders/link.service';
import { PaginatedList } from '../../data/paginated-list.model'; import { PaginatedList } from '../../data/paginated-list.model';
import { ResponseParsingService } from '../../data/parsing.service'; import { ResponseParsingService } from '../../data/parsing.service';
import { RemoteData } from '../../data/remote-data'; import { RemoteData } from '../../data/remote-data';
import { GetRequest} from '../../data/request.models'; import { GetRequest } from '../../data/request.models';
import { RequestService } from '../../data/request.service'; import { RequestService } from '../../data/request.service';
import { DSpaceObject } from '../dspace-object.model'; import { DSpaceObject } from '../dspace-object.model';
import { GenericConstructor } from '../generic-constructor'; import { GenericConstructor } from '../generic-constructor';
@@ -21,7 +19,6 @@ import { SearchResponseParsingService } from '../../data/search-response-parsing
import { SearchObjects } from '../../../shared/search/models/search-objects.model'; import { SearchObjects } from '../../../shared/search/models/search-objects.model';
import { FacetValueResponseParsingService } from '../../data/facet-value-response-parsing.service'; import { FacetValueResponseParsingService } from '../../data/facet-value-response-parsing.service';
import { PaginatedSearchOptions } from '../../../shared/search/models/paginated-search-options.model'; import { PaginatedSearchOptions } from '../../../shared/search/models/paginated-search-options.model';
import { CommunityDataService } from '../../data/community-data.service';
import { ViewMode } from '../view-mode.model'; import { ViewMode } from '../view-mode.model';
import { DSpaceObjectDataService } from '../../data/dspace-object-data.service'; import { DSpaceObjectDataService } from '../../data/dspace-object-data.service';
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
@@ -34,31 +31,19 @@ import { FacetValues } from '../../../shared/search/models/facet-values.model';
import { PaginationService } from '../../pagination/pagination.service'; import { PaginationService } from '../../pagination/pagination.service';
import { SearchConfigurationService } from './search-configuration.service'; import { SearchConfigurationService } from './search-configuration.service';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
import { DataService } from '../../data/data.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../../cache/object-cache.service';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DSOChangeAnalyzer } from '../../data/dso-change-analyzer.service';
import { RestRequest } from '../../data/rest-request.model'; import { RestRequest } from '../../data/rest-request.model';
import { CoreState } from '../../core-state.model'; import { BaseDataService } from '../../data/base/base-data.service';
/** /**
* A class that lets us delegate some methods to DataService * A limited data service implementation for the 'discover' endpoint
* - Overrides {@link BaseDataService.addEmbedParams} in order to make it public
*
* Doesn't use any of the service's dependencies, they are initialized as undefined
* Therefore, equest/response handling methods won't work even though they're defined
*/ */
class DataServiceImpl extends DataService<any> { class SearchDataService extends BaseDataService<any> {
protected linkPath = 'discover'; constructor() {
super('discover', undefined, undefined, undefined, undefined);
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: DSOChangeAnalyzer<any>) {
super();
} }
/** /**
@@ -99,31 +84,20 @@ export class SearchService implements OnDestroy {
private sub; private sub;
/** /**
* Instance of DataServiceImpl that lets us delegate some methods to DataService * Instance of SearchDataService to forward data service methods to
*/ */
private searchDataService: DataServiceImpl; private searchDataService: SearchDataService;
constructor(private router: Router, constructor(
private routeService: RouteService, private routeService: RouteService,
protected requestService: RequestService, protected requestService: RequestService,
private rdb: RemoteDataBuildService, private rdb: RemoteDataBuildService,
private linkService: LinkService, private halService: HALEndpointService,
private halService: HALEndpointService, private dspaceObjectService: DSpaceObjectDataService,
private communityService: CommunityDataService, private paginationService: PaginationService,
private dspaceObjectService: DSpaceObjectDataService, private searchConfigurationService: SearchConfigurationService,
private paginationService: PaginationService,
private searchConfigurationService: SearchConfigurationService
) { ) {
this.searchDataService = new DataServiceImpl( this.searchDataService = new SearchDataService();
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined
);
} }
/** /**

View File

@@ -1,41 +1,38 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { DataService } from '../data/data.service';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
import { USAGE_REPORT } from './models/usage-report.resource-type'; import { USAGE_REPORT } from './models/usage-report.resource-type';
import { UsageReport } from './models/usage-report.model'; import { UsageReport } from './models/usage-report.model';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { getRemoteDataPayload, getFirstSucceededRemoteData } from '../shared/operators'; import { getFirstSucceededRemoteData, getRemoteDataPayload } from '../shared/operators';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { CoreState } from '../core-state.model'; import { IdentifiableDataService } from '../data/base/identifiable-data.service';
import { SearchData, SearchDataImpl } from '../data/base/search-data';
import { FindListOptions } from '../data/find-list-options.model';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { RemoteData } from '../data/remote-data';
import { PaginatedList } from '../data/paginated-list.model';
/** /**
* A service to retrieve {@link UsageReport}s from the REST API * A service to retrieve {@link UsageReport}s from the REST API
*/ */
@Injectable() @Injectable()
@dataService(USAGE_REPORT) @dataService(USAGE_REPORT)
export class UsageReportService extends DataService<UsageReport> { export class UsageReportService extends IdentifiableDataService<UsageReport> implements SearchData<UsageReport> {
private searchData: SearchDataImpl<UsageReport>;
protected linkPath = 'usagereports';
constructor( constructor(
protected comparator: DefaultChangeAnalyzer<UsageReport>,
protected halService: HALEndpointService,
protected http: HttpClient,
protected notificationsService: NotificationsService,
protected objectCache: ObjectCacheService,
protected rdbService: RemoteDataBuildService,
protected requestService: RequestService, protected requestService: RequestService,
protected store: Store<CoreState>, protected rdbService: RemoteDataBuildService,
protected halService: HALEndpointService,
protected objectCache: ObjectCacheService,
) { ) {
super(); super('usagereports', requestService, rdbService, objectCache, halService);
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
} }
getStatistic(scope: string, type: string): Observable<UsageReport> { getStatistic(scope: string, type: string): Observable<UsageReport> {
@@ -47,10 +44,12 @@ export class UsageReportService extends DataService<UsageReport> {
searchStatistics(uri: string, page: number, size: number): Observable<UsageReport[]> { searchStatistics(uri: string, page: number, size: number): Observable<UsageReport[]> {
return this.searchBy('object', { return this.searchBy('object', {
searchParams: [{ searchParams: [
fieldName: `uri`, {
fieldValue: uri, fieldName: `uri`,
}], fieldValue: uri,
},
],
currentPage: page, currentPage: page,
elementsPerPage: size, elementsPerPage: size,
}, true, false).pipe( }, true, false).pipe(
@@ -59,4 +58,8 @@ export class UsageReportService extends DataService<UsageReport> {
map((list) => list.page), map((list) => list.page),
); );
} }
public searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<UsageReport>[]): Observable<RemoteData<PaginatedList<UsageReport>>> {
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
} }

View File

@@ -1,14 +1,12 @@
import { DSpaceObject } from './../../shared/dspace-object.model'; import { followLink } from '../../../shared/utils/follow-link-config.model';
import { followLink } from './../../../shared/utils/follow-link-config.model';
import { ChildHALResource } from './../../shared/child-hal-resource.model';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { switchMap } from 'rxjs/operators'; import { switchMap } from 'rxjs/operators';
import { DataService } from '../../data/data.service';
import { RemoteData } from '../../data/remote-data'; import { RemoteData } from '../../data/remote-data';
import { getFirstCompletedRemoteData } from '../../shared/operators'; import { getFirstCompletedRemoteData } from '../../shared/operators';
import { IdentifiableDataService } from '../../data/base/identifiable-data.service';
/** /**
* This class represents a resolver that requests a specific item before the route is activated * This class represents a resolver that requests a specific item before the route is activated
@@ -16,8 +14,8 @@ import { getFirstCompletedRemoteData } from '../../shared/operators';
@Injectable() @Injectable()
export class SubmissionObjectResolver<T> implements Resolve<RemoteData<T>> { export class SubmissionObjectResolver<T> implements Resolve<RemoteData<T>> {
constructor( constructor(
protected dataService: DataService<any>, protected dataService: IdentifiableDataService<any>,
protected store: Store<any> protected store: Store<any>,
) { ) {
} }

View File

@@ -1,34 +1,49 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { DataService } from '../data/data.service';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { SUBMISSION_CC_LICENSE } from './models/submission-cc-licence.resource-type'; import { SUBMISSION_CC_LICENSE } from './models/submission-cc-licence.resource-type';
import { SubmissionCcLicence } from './models/submission-cc-license.model'; import { SubmissionCcLicence } from './models/submission-cc-license.model';
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; import { BaseDataService } from '../data/base/base-data.service';
import { CoreState } from '../core-state.model'; import { FindAllData } from '../data/base/find-all-data';
import { FindListOptions } from '../data/find-list-options.model';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { Observable } from 'rxjs';
import { RemoteData } from '../data/remote-data';
import { PaginatedList } from '../data/paginated-list.model';
@Injectable() @Injectable()
@dataService(SUBMISSION_CC_LICENSE) @dataService(SUBMISSION_CC_LICENSE)
export class SubmissionCcLicenseDataService extends DataService<SubmissionCcLicence> { export class SubmissionCcLicenseDataService extends BaseDataService<SubmissionCcLicence> implements FindAllData<SubmissionCcLicence> {
protected linkPath = 'submissioncclicenses'; protected linkPath = 'submissioncclicenses';
constructor( constructor(
protected comparator: DefaultChangeAnalyzer<SubmissionCcLicence>,
protected halService: HALEndpointService,
protected http: HttpClient,
protected notificationsService: NotificationsService,
protected objectCache: ObjectCacheService,
protected rdbService: RemoteDataBuildService,
protected requestService: RequestService, protected requestService: RequestService,
protected store: Store<CoreState>, protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
) { ) {
super(); super('submissioncclicenses', requestService, rdbService, objectCache, halService);
}
/**
* 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 useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>>}
* Return an observable that emits object list
*/
public findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<SubmissionCcLicence>[]): Observable<RemoteData<PaginatedList<SubmissionCcLicence>>> {
return undefined;
} }
} }

View File

@@ -1,40 +1,36 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { DataService } from '../data/data.service';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
import { SubmissionCcLicenceUrl } from './models/submission-cc-license-url.model'; import { SubmissionCcLicenceUrl } from './models/submission-cc-license-url.model';
import { SUBMISSION_CC_LICENSE_URL } from './models/submission-cc-licence-link.resource-type'; import { SUBMISSION_CC_LICENSE_URL } from './models/submission-cc-licence-link.resource-type';
import { Field, Option, SubmissionCcLicence } from './models/submission-cc-license.model'; import { Field, Option, SubmissionCcLicence } from './models/submission-cc-license.model';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { filter, map, switchMap } from 'rxjs/operators'; import { map, switchMap } from 'rxjs/operators';
import { getRemoteDataPayload, getFirstSucceededRemoteData } from '../shared/operators'; import { getRemoteDataPayload, getFirstSucceededRemoteData } from '../shared/operators';
import { isNotEmpty } from '../../shared/empty.util'; import { BaseDataService } from '../data/base/base-data.service';
import { CoreState } from '../core-state.model'; import { SearchData, SearchDataImpl } from '../data/base/search-data';
import { FindListOptions } from '../data/find-list-options.model';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { RemoteData } from '../data/remote-data';
import { PaginatedList } from '../data/paginated-list.model';
@Injectable() @Injectable()
@dataService(SUBMISSION_CC_LICENSE_URL) @dataService(SUBMISSION_CC_LICENSE_URL)
export class SubmissionCcLicenseUrlDataService extends DataService<SubmissionCcLicenceUrl> { export class SubmissionCcLicenseUrlDataService extends BaseDataService<SubmissionCcLicenceUrl> implements SearchData<SubmissionCcLicenceUrl> {
private searchData: SearchData<SubmissionCcLicenceUrl>;
protected linkPath = 'submissioncclicenseUrls-search';
constructor( constructor(
protected comparator: DefaultChangeAnalyzer<SubmissionCcLicenceUrl>,
protected halService: HALEndpointService,
protected http: HttpClient,
protected notificationsService: NotificationsService,
protected objectCache: ObjectCacheService,
protected rdbService: RemoteDataBuildService,
protected requestService: RequestService, protected requestService: RequestService,
protected store: Store<CoreState>, protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
) { ) {
super(); super('submissioncclicenseUrls-search', requestService, rdbService, objectCache, halService);
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive, (href, searchMethod) => `${href}/${searchMethod}`);
} }
/** /**
@@ -68,9 +64,34 @@ export class SubmissionCcLicenseUrlDataService extends DataService<SubmissionCcL
); );
} }
protected getSearchEndpoint(searchMethod: string): Observable<string> { /**
return this.halService.getEndpoint(`${this.linkPath}`).pipe( * Create the HREF for a specific object's search method with given options object
filter((href: string) => isNotEmpty(href)), *
map((href: string) => `${href}/${searchMethod}`)); * @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @return {Observable<string>}
* Return an observable that emits created HREF
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
*/
public getSearchByHref(searchMethod: string, options?: FindListOptions, ...linksToFollow: FollowLinkConfig<SubmissionCcLicenceUrl>[]): Observable<string> {
return this.searchData.getSearchByHref(searchMethod, options, ...linksToFollow);
}
/**
* Make a new FindListRequest with given search method
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>}
* Return an observable that emits response from the server
*/
public searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<SubmissionCcLicenceUrl>[]): Observable<RemoteData<PaginatedList<SubmissionCcLicenceUrl>>> {
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
} }
} }

View File

@@ -7,11 +7,11 @@ import { SubmissionObject } from './models/submission-object.model';
import { SubmissionScopeType } from './submission-scope-type'; import { SubmissionScopeType } from './submission-scope-type';
import { WorkflowItemDataService } from './workflowitem-data.service'; import { WorkflowItemDataService } from './workflowitem-data.service';
import { WorkspaceitemDataService } from './workspaceitem-data.service'; import { WorkspaceitemDataService } from './workspaceitem-data.service';
import { DataService } from '../data/data.service';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { environment } from '../../../environments/environment'; import { environment } from '../../../environments/environment';
import { RequestEntryState } from '../data/request-entry-state.model'; import { RequestEntryState } from '../data/request-entry-state.model';
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
/** /**
* A service to retrieve submission objects (WorkspaceItem/WorkflowItem) * A service to retrieve submission objects (WorkspaceItem/WorkflowItem)
@@ -34,7 +34,7 @@ export class SubmissionObjectDataService {
* @param id The identifier for the object * @param id The identifier for the object
*/ */
getHrefByID(id): Observable<string> { getHrefByID(id): Observable<string> {
const dataService: DataService<SubmissionObject> = this.submissionService.getSubmissionScope() === SubmissionScopeType.WorkspaceItem ? this.workspaceitemDataService : this.workflowItemDataService; const dataService: IdentifiableDataService<SubmissionObject> = this.submissionService.getSubmissionScope() === SubmissionScopeType.WorkspaceItem ? this.workspaceitemDataService : this.workflowItemDataService;
return this.halService.getEndpoint(dataService.getLinkPath()).pipe( return this.halService.getEndpoint(dataService.getLinkPath()).pipe(
map((endpoint: string) => dataService.getIDHref(endpoint, encodeURIComponent(id)))); map((endpoint: string) => dataService.getIDHref(endpoint, encodeURIComponent(id))));

View File

@@ -0,0 +1,16 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
import { testFindAllDataImplementation } from '../../data/base/find-all-data.spec';
import { VocabularyEntryDetailsDataService } from './vocabulary-entry-details.data.service';
import { testSearchDataImplementation } from '../../data/base/search-data.spec';
describe('VocabularyEntryDetailsDataService', () => {
testFindAllDataImplementation(new VocabularyEntryDetailsDataService(null, null, null, null));
testSearchDataImplementation(new VocabularyEntryDetailsDataService(null, null, null, null));
});

View File

@@ -0,0 +1,104 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
import { IdentifiableDataService } from '../../data/base/identifiable-data.service';
import { VocabularyEntryDetail } from './models/vocabulary-entry-detail.model';
import { RequestService } from '../../data/request.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 { FindAllData, FindAllDataImpl } from '../../data/base/find-all-data';
import { FindListOptions } from '../../data/find-list-options.model';
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
import { Observable } from 'rxjs';
import { RemoteData } from '../../data/remote-data';
import { PaginatedList } from '../../data/paginated-list.model';
import { SearchData, SearchDataImpl } from '../../data/base/search-data';
import { Injectable } from '@angular/core';
import { dataService } from '../../cache/builders/build-decorators';
import { VOCABULARY_ENTRY_DETAIL } from './models/vocabularies.resource-type';
@Injectable()
@dataService(VOCABULARY_ENTRY_DETAIL)
export class VocabularyEntryDetailsDataService extends IdentifiableDataService<VocabularyEntryDetail> implements FindAllData<VocabularyEntryDetail>, SearchData<VocabularyEntryDetail> {
private findAllData: FindAllData<VocabularyEntryDetail>;
private searchData: SearchData<VocabularyEntryDetail>;
constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
) {
super('vocabularyEntryDetails', requestService, rdbService, objectCache, halService);
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
}
/**
* Create the HREF with given options object
*
* @param options The [[FindListOptions]] object
* @param linkPath The link path for the object
* @return {Observable<string>}
* Return an observable that emits created HREF
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
*/
public getFindAllHref(options: FindListOptions, linkPath?: string, ...linksToFollow: FollowLinkConfig<VocabularyEntryDetail>[]): Observable<string> {
return this.findAllData.getFindAllHref(options, linkPath, ...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 useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>>}
* Return an observable that emits object list
*/
public findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<VocabularyEntryDetail>[]): Observable<RemoteData<PaginatedList<VocabularyEntryDetail>>> {
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Make a new FindListRequest with given search method
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>}
* Return an observable that emits response from the server
*/
public searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<VocabularyEntryDetail>[]): Observable<RemoteData<PaginatedList<VocabularyEntryDetail>>> {
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Create the HREF for a specific object's search method with given options object
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @return {Observable<string>}
* Return an observable that emits created HREF
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
*/
public getSearchByHref(searchMethod: string, options?: FindListOptions, ...linksToFollow): Observable<string> {
return this.searchData.getSearchByHref(searchMethod, options, ...linksToFollow);
}
}

View File

@@ -0,0 +1,14 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
import { VocabularyDataService } from './vocabulary.data.service';
import { testFindAllDataImplementation } from '../../data/base/find-all-data.spec';
describe('VocabularyDataService', () => {
testFindAllDataImplementation(new VocabularyDataService(null, null, null, null));
});

View File

@@ -0,0 +1,70 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
import { IdentifiableDataService } from '../../data/base/identifiable-data.service';
import { RequestService } from '../../data/request.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 { Vocabulary } from './models/vocabulary.model';
import { FindAllData, FindAllDataImpl } from '../../data/base/find-all-data';
import { FindListOptions } from '../../data/find-list-options.model';
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
import { Observable } from 'rxjs';
import { RemoteData } from '../../data/remote-data';
import { PaginatedList } from '../../data/paginated-list.model';
import { Injectable } from '@angular/core';
import { dataService } from '../../cache/builders/build-decorators';
import { VOCABULARY } from './models/vocabularies.resource-type';
@Injectable()
@dataService(VOCABULARY)
export class VocabularyDataService extends IdentifiableDataService<Vocabulary> implements FindAllData<Vocabulary> {
private findAllData: FindAllData<Vocabulary>;
constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
) {
super('vocabularies', requestService, rdbService, objectCache, halService);
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
}
/**
* Create the HREF with given options object
*
* @param options The [[FindListOptions]] object
* @param linkPath The link path for the object
* @return {Observable<string>}
* Return an observable that emits created HREF
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
*/
public getFindAllHref(options: FindListOptions, linkPath?: string, ...linksToFollow: FollowLinkConfig<Vocabulary>[]): Observable<string> {
return this.findAllData.getFindAllHref(options, linkPath, ...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 useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>>}
* Return an observable that emits object list
*/
public findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<Vocabulary>[]): Observable<RemoteData<PaginatedList<Vocabulary>>> {
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
}

View File

@@ -1,10 +1,7 @@
import { HttpClient } from '@angular/common/http';
import { cold, getTestScheduler, hot } from 'jasmine-marbles'; import { cold, getTestScheduler, hot } from 'jasmine-marbles';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { TestScheduler } from 'rxjs/testing'; import { TestScheduler } from 'rxjs/testing';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../../cache/object-cache.service'; import { ObjectCacheService } from '../../cache/object-cache.service';
import { HALEndpointService } from '../../shared/hal-endpoint.service'; import { HALEndpointService } from '../../shared/hal-endpoint.service';
@@ -26,6 +23,8 @@ import { HrefOnlyDataService } from '../../data/href-only-data.service';
import { getMockHrefOnlyDataService } from '../../../shared/mocks/href-only-data.service.mock'; import { getMockHrefOnlyDataService } from '../../../shared/mocks/href-only-data.service.mock';
import { createPaginatedList } from '../../../shared/testing/utils.test'; import { createPaginatedList } from '../../../shared/testing/utils.test';
import { RequestEntry } from '../../data/request-entry.model'; import { RequestEntry } from '../../data/request-entry.model';
import { VocabularyDataService } from './vocabulary.data.service';
import { VocabularyEntryDetailsDataService } from './vocabulary-entry-details.data.service';
describe('VocabularyService', () => { describe('VocabularyService', () => {
let scheduler: TestScheduler; let scheduler: TestScheduler;
@@ -203,24 +202,14 @@ describe('VocabularyService', () => {
response: { isSuccessful: successful, payload: arrayEntries } as any response: { isSuccessful: successful, payload: arrayEntries } as any
} as RequestEntry); } as RequestEntry);
}; };
objectCache = {} as ObjectCacheService;
const notificationsService = {} as NotificationsService;
const http = {} as HttpClient;
const comparator = {} as any;
const comparatorEntry = {} as any;
function initTestService() { function initTestService() {
hrefOnlyDataService = getMockHrefOnlyDataService(); hrefOnlyDataService = getMockHrefOnlyDataService();
return new VocabularyService( return new VocabularyService(
requestService, requestService,
rdbService, new VocabularyDataService(requestService, rdbService, objectCache, halService),
objectCache, new VocabularyEntryDetailsDataService(requestService, rdbService, objectCache, halService),
halService,
notificationsService,
hrefOnlyDataService,
http,
comparator,
comparatorEntry
); );
} }
@@ -229,7 +218,7 @@ describe('VocabularyService', () => {
scheduler = getTestScheduler(); scheduler = getTestScheduler();
halService = jasmine.createSpyObj('halService', { halService = jasmine.createSpyObj('halService', {
getEndpoint: cold('a', { a: endpointURL }) getEndpoint: cold('a', { a: endpointURL }),
}); });
}); });
@@ -237,7 +226,7 @@ describe('VocabularyService', () => {
service = null; service = null;
}); });
describe('', () => { describe('vocabularies', () => {
beforeEach(() => { beforeEach(() => {
responseCacheEntry = new RequestEntry(); responseCacheEntry = new RequestEntry();
responseCacheEntry.request = { href: 'https://rest.api/' } as any; responseCacheEntry.request = { href: 'https://rest.api/' } as any;
@@ -255,7 +244,7 @@ describe('VocabularyService', () => {
a: vocabularyRD a: vocabularyRD
}), }),
buildList: hot('a|', { buildList: hot('a|', {
a: paginatedListRD a: paginatedListRD,
}), }),
}); });
@@ -264,8 +253,6 @@ describe('VocabularyService', () => {
spyOn((service as any).vocabularyDataService, 'findById').and.callThrough(); spyOn((service as any).vocabularyDataService, 'findById').and.callThrough();
spyOn((service as any).vocabularyDataService, 'findAll').and.callThrough(); spyOn((service as any).vocabularyDataService, 'findAll').and.callThrough();
spyOn((service as any).vocabularyDataService, 'findByHref').and.callThrough(); spyOn((service as any).vocabularyDataService, 'findByHref').and.callThrough();
spyOn((service as any).vocabularyDataService, 'searchBy').and.callThrough();
spyOn((service as any).vocabularyDataService, 'getSearchByHref').and.returnValue(observableOf(searchRequestURL));
spyOn((service as any).vocabularyDataService, 'getFindAllHref').and.returnValue(observableOf(entriesRequestURL)); spyOn((service as any).vocabularyDataService, 'getFindAllHref').and.returnValue(observableOf(entriesRequestURL));
}); });
@@ -274,7 +261,7 @@ describe('VocabularyService', () => {
}); });
describe('findVocabularyById', () => { describe('findVocabularyById', () => {
it('should proxy the call to vocabularyDataService.findVocabularyById', () => { it('should proxy the call to vocabularyDataService.findById', () => {
scheduler.schedule(() => service.findVocabularyById(vocabularyId)); scheduler.schedule(() => service.findVocabularyById(vocabularyId));
scheduler.flush(); scheduler.flush();
@@ -325,7 +312,7 @@ describe('VocabularyService', () => {
}); });
}); });
describe('', () => { describe('vocabulary entries', () => {
beforeEach(() => { beforeEach(() => {
requestService = getMockRequestService(getRequestEntries$(true)); requestService = getMockRequestService(getRequestEntries$(true));

View File

@@ -1,104 +1,38 @@
/* eslint-disable max-classes-per-file */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, switchMap, mergeMap } from 'rxjs/operators'; import { map, switchMap, mergeMap } from 'rxjs/operators';
import { FollowLinkConfig, followLink } from '../../../shared/utils/follow-link-config.model'; import { FollowLinkConfig, followLink } 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 { RequestService } from '../../data/request.service';
import { HALEndpointService } from '../../shared/hal-endpoint.service';
import { RemoteData } from '../../data/remote-data'; import { RemoteData } from '../../data/remote-data';
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
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.model'; import { PaginatedList } from '../../data/paginated-list.model';
import { Vocabulary } from './models/vocabulary.model'; import { Vocabulary } from './models/vocabulary.model';
import { VOCABULARY } from './models/vocabularies.resource-type';
import { VocabularyEntry } from './models/vocabulary-entry.model'; import { VocabularyEntry } from './models/vocabulary-entry.model';
import { isNotEmpty } from '../../../shared/empty.util'; import { isNotEmpty } from '../../../shared/empty.util';
import { import {
getFirstSucceededRemoteDataPayload, getFirstSucceededRemoteDataPayload,
getFirstSucceededRemoteListPayload getFirstSucceededRemoteListPayload,
} from '../../shared/operators'; } from '../../shared/operators';
import { VocabularyFindOptions } from './models/vocabulary-find-options.model'; import { VocabularyFindOptions } from './models/vocabulary-find-options.model';
import { VocabularyEntryDetail } from './models/vocabulary-entry-detail.model'; import { VocabularyEntryDetail } from './models/vocabulary-entry-detail.model';
import { RequestParam } from '../../cache/models/request-param.model'; import { RequestParam } from '../../cache/models/request-param.model';
import { VocabularyOptions } from './models/vocabulary-options.model'; import { VocabularyOptions } from './models/vocabulary-options.model';
import { PageInfo } from '../../shared/page-info.model'; import { PageInfo } from '../../shared/page-info.model';
import { HrefOnlyDataService } from '../../data/href-only-data.service';
import { CoreState } from '../../core-state.model';
import { FindListOptions } from '../../data/find-list-options.model'; import { FindListOptions } from '../../data/find-list-options.model';
import { VocabularyEntryDetailsDataService } from './vocabulary-entry-details.data.service';
import { VocabularyDataService } from './vocabulary.data.service';
/**
* A private DataService implementation to delegate specific methods to.
*/
class VocabularyDataServiceImpl 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 private DataService implementation to delegate specific methods to.
*/
class VocabularyEntryDetailDataServiceImpl extends DataService<VocabularyEntryDetail> {
protected linkPath = 'vocabularyEntryDetails';
constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected hrefOnlyDataService: HrefOnlyDataService,
protected http: HttpClient,
protected comparator: ChangeAnalyzer<VocabularyEntryDetail>) {
super();
}
}
/** /**
* A service responsible for fetching/sending data from/to the REST API on the vocabularies endpoint * A service responsible for fetching/sending data from/to the REST API on the vocabularies endpoint
*/ */
@Injectable() @Injectable()
@dataService(VOCABULARY)
export class VocabularyService { export class VocabularyService {
protected searchTopMethod = 'top'; protected searchTopMethod = 'top';
private vocabularyDataService: VocabularyDataServiceImpl;
private vocabularyEntryDetailDataService: VocabularyEntryDetailDataServiceImpl;
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected vocabularyDataService: VocabularyDataService,
protected objectCache: ObjectCacheService, protected vocabularyEntryDetailDataService: VocabularyEntryDetailsDataService,
protected halService: HALEndpointService, ) {
protected notificationsService: NotificationsService,
protected hrefOnlyDataService: HrefOnlyDataService,
protected http: HttpClient,
protected comparatorVocabulary: DefaultChangeAnalyzer<Vocabulary>,
protected comparatorEntry: DefaultChangeAnalyzer<VocabularyEntryDetail>) {
this.vocabularyDataService = new VocabularyDataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparatorVocabulary);
this.vocabularyEntryDetailDataService = new VocabularyEntryDetailDataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, hrefOnlyDataService, http, comparatorEntry);
} }
/** /**

View File

@@ -82,14 +82,11 @@ describe('WorkflowItemDataService test', () => {
function initTestService() { function initTestService() {
hrefOnlyDataService = getMockHrefOnlyDataService(); hrefOnlyDataService = getMockHrefOnlyDataService();
return new WorkflowItemDataService( return new WorkflowItemDataService(
comparatorEntry,
halService,
http,
notificationsService,
requestService, requestService,
rdbService, rdbService,
objectCache, objectCache,
store halService,
notificationsService,
); );
} }

View File

@@ -1,17 +1,12 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Store } from '@ngrx/store';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { DataService } from '../data/data.service';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { WorkflowItem } from './models/workflowitem.model'; import { WorkflowItem } from './models/workflowitem.model';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { DeleteByIDRequest } from '../data/request.models'; import { DeleteByIDRequest } from '../data/request.models';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { find, map } from 'rxjs/operators'; import { find, map } from 'rxjs/operators';
import { hasValue } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
@@ -21,29 +16,36 @@ import { getFirstCompletedRemoteData } from '../shared/operators';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { WorkspaceItem } from './models/workspaceitem.model'; import { WorkspaceItem } from './models/workspaceitem.model';
import { RequestParam } from '../cache/models/request-param.model'; import { RequestParam } from '../cache/models/request-param.model';
import { CoreState } from '../core-state.model';
import { FindListOptions } from '../data/find-list-options.model'; import { FindListOptions } from '../data/find-list-options.model';
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
import { SearchData, SearchDataImpl } from '../data/base/search-data';
import { DeleteData, DeleteDataImpl } from '../data/base/delete-data';
import { PaginatedList } from '../data/paginated-list.model';
/** /**
* A service that provides methods to make REST requests with workflow items endpoint. * A service that provides methods to make REST requests with workflow items endpoint.
*/ */
@Injectable() @Injectable()
@dataService(WorkflowItem.type) @dataService(WorkflowItem.type)
export class WorkflowItemDataService extends DataService<WorkflowItem> { export class WorkflowItemDataService extends IdentifiableDataService<WorkflowItem> implements SearchData<WorkflowItem>, DeleteData<WorkflowItem> {
protected linkPath = 'workflowitems'; protected linkPath = 'workflowitems';
protected searchByItemLinkPath = 'item'; protected searchByItemLinkPath = 'item';
protected responseMsToLive = 10 * 1000; protected responseMsToLive = 10 * 1000;
private searchData: SearchDataImpl<WorkflowItem>;
private deleteData: DeleteDataImpl<WorkflowItem>;
constructor( constructor(
protected comparator: DSOChangeAnalyzer<WorkflowItem>,
protected halService: HALEndpointService,
protected http: HttpClient,
protected notificationsService: NotificationsService,
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected store: Store<CoreState>) { protected halService: HALEndpointService,
super(); protected notificationsService: NotificationsService,
) {
super('workspaceitems', requestService, rdbService, objectCache, halService);
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
} }
/** /**
@@ -78,8 +80,8 @@ export class WorkflowItemDataService extends DataService<WorkflowItem> {
const requestId = this.requestService.generateRequestId(); const requestId = this.requestService.generateRequestId();
const hrefObs = this.halService.getEndpoint(this.linkPath).pipe( const hrefObs = this.halService.getEndpoint(this.linkPath).pipe(
map((endpoint: string) => this.getIDHref(endpoint, id)), map((endpoint: string) => this.deleteData.getIDHref(endpoint, id)),
map((endpoint: string) => endpoint + '?expunge=' + expunge) map((endpoint: string) => endpoint + '?expunge=' + expunge),
); );
hrefObs.pipe( hrefObs.pipe(
@@ -110,4 +112,47 @@ export class WorkflowItemDataService extends DataService<WorkflowItem> {
return this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); return this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
} }
/**
* Create the HREF for a specific object's search method with given options object
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @return {Observable<string>}
* Return an observable that emits created HREF
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
*/
public getSearchByHref(searchMethod: string, options?: FindListOptions, ...linksToFollow): Observable<string> {
return this.searchData.getSearchByHref(searchMethod, options, ...linksToFollow);
}
/**
* Make a new FindListRequest with given search method
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>}
* Return an observable that emits response from the server
*/
public searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<WorkspaceItem>[]): Observable<RemoteData<PaginatedList<WorkspaceItem>>> {
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Delete an existing object on the server
* @param href The self link of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
* Only emits once all request related to the DSO has been invalidated.
*/
public deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
}
} }

View File

@@ -82,14 +82,11 @@ describe('WorkspaceitemDataService test', () => {
function initTestService() { function initTestService() {
hrefOnlyDataService = getMockHrefOnlyDataService(); hrefOnlyDataService = getMockHrefOnlyDataService();
return new WorkspaceitemDataService( return new WorkspaceitemDataService(
comparatorEntry,
halService,
http,
notificationsService,
requestService, requestService,
rdbService, rdbService,
objectCache, objectCache,
store halService,
notificationsService,
); );
} }

View File

@@ -1,42 +1,44 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Store } from '@ngrx/store';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { DataService } from '../data/data.service';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
import { WorkspaceItem } from './models/workspaceitem.model'; import { WorkspaceItem } from './models/workspaceitem.model';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { RequestParam } from '../cache/models/request-param.model'; import { RequestParam } from '../cache/models/request-param.model';
import { CoreState } from '../core-state.model';
import { FindListOptions } from '../data/find-list-options.model'; import { FindListOptions } from '../data/find-list-options.model';
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
import { SearchData, SearchDataImpl } from '../data/base/search-data';
import { PaginatedList } from '../data/paginated-list.model';
import { DeleteData, DeleteDataImpl } from '../data/base/delete-data';
import { NoContent } from '../shared/NoContent.model';
/** /**
* A service that provides methods to make REST requests with workspaceitems endpoint. * A service that provides methods to make REST requests with workspaceitems endpoint.
*/ */
@Injectable() @Injectable()
@dataService(WorkspaceItem.type) @dataService(WorkspaceItem.type)
export class WorkspaceitemDataService extends DataService<WorkspaceItem> { export class WorkspaceitemDataService extends IdentifiableDataService<WorkspaceItem> implements SearchData<WorkspaceItem>, DeleteData<WorkspaceItem> {
protected linkPath = 'workspaceitems';
protected searchByItemLinkPath = 'item'; protected searchByItemLinkPath = 'item';
private searchData: SearchDataImpl<WorkspaceItem>;
private deleteData: DeleteDataImpl<WorkspaceItem>;
constructor( constructor(
protected comparator: DSOChangeAnalyzer<WorkspaceItem>,
protected halService: HALEndpointService,
protected http: HttpClient,
protected notificationsService: NotificationsService,
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected store: Store<CoreState>) { protected halService: HALEndpointService,
super(); protected notificationsService: NotificationsService,
) {
super('workspaceitems', requestService, rdbService, objectCache, halService);
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
} }
/** /**
@@ -57,4 +59,60 @@ export class WorkspaceitemDataService extends DataService<WorkspaceItem> {
return this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); return this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
} }
/**
* Create the HREF for a specific object's search method with given options object
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @return {Observable<string>}
* Return an observable that emits created HREF
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
*/
public getSearchByHref(searchMethod: string, options?: FindListOptions, ...linksToFollow): Observable<string> {
return this.searchData.getSearchByHref(searchMethod, options, ...linksToFollow);
}
/**
* Make a new FindListRequest with given search method
*
* @param searchMethod The search method for the object
* @param options The [[FindListOptions]] object
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>}
* Return an observable that emits response from the server
*/
public searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<WorkspaceItem>[]): Observable<RemoteData<PaginatedList<WorkspaceItem>>> {
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Delete an existing object on the server
* @param objectId The id of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
*/
public delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.delete(objectId, copyVirtualMetadata);
}
/**
* Delete an existing object on the server
* @param href The self link of the object to be removed
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
* metadata should be saved as real metadata
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
* errorMessage, timeCompleted, etc
* Only emits once all request related to the DSO has been invalidated.
*/
public deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
}
} }

View File

@@ -1,11 +1,8 @@
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { HttpHeaders } from '@angular/common/http';
import { Store } from '@ngrx/store';
import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { getMockRequestService } from '../../shared/mocks/request.service.mock';
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { ClaimedTaskDataService } from './claimed-task-data.service'; import { ClaimedTaskDataService } from './claimed-task-data.service';
import { of as observableOf } from 'rxjs/internal/observable/of'; import { of as observableOf } from 'rxjs/internal/observable/of';
import { RequestParam } from '../cache/models/request-param.model'; import { RequestParam } from '../cache/models/request-param.model';
@@ -13,7 +10,6 @@ import { getTestScheduler } from 'jasmine-marbles';
import { TestScheduler } from 'rxjs/testing'; import { TestScheduler } from 'rxjs/testing';
import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { HttpOptions } from '../dspace-rest/dspace-rest.service';
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
import { CoreState } from '../core-state.model';
import { FindListOptions } from '../data/find-list-options.model'; import { FindListOptions } from '../data/find-list-options.model';
describe('ClaimedTaskDataService', () => { describe('ClaimedTaskDataService', () => {
@@ -25,9 +21,6 @@ describe('ClaimedTaskDataService', () => {
const requestService: any = getMockRequestService(); const requestService: any = getMockRequestService();
const halService: any = new HALEndpointServiceStub(taskEndpoint); const halService: any = new HALEndpointServiceStub(taskEndpoint);
const rdbService = {} as RemoteDataBuildService; const rdbService = {} as RemoteDataBuildService;
const notificationsService = {} as NotificationsService;
const http = {} as HttpClient;
const comparator = {} as any;
const objectCache = { const objectCache = {
addPatch: () => { addPatch: () => {
/* empty */ /* empty */
@@ -36,18 +29,13 @@ describe('ClaimedTaskDataService', () => {
/* empty */ /* empty */
} }
} as any; } as any;
const store = {} as Store<CoreState>;
function initTestService(): ClaimedTaskDataService { function initTestService(): ClaimedTaskDataService {
return new ClaimedTaskDataService( return new ClaimedTaskDataService(
requestService, requestService,
rdbService, rdbService,
store,
objectCache, objectCache,
halService, halService,
notificationsService,
http,
comparator
); );
} }

View File

@@ -1,13 +1,9 @@
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { ClaimedTask } from './models/claimed-task-object.model'; import { ClaimedTask } from './models/claimed-task-object.model';
@@ -18,7 +14,6 @@ import { RemoteData } from '../data/remote-data';
import { RequestParam } from '../cache/models/request-param.model'; import { RequestParam } from '../cache/models/request-param.model';
import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { HttpOptions } from '../dspace-rest/dspace-rest.service';
import { getFirstSucceededRemoteData } from '../shared/operators'; import { getFirstSucceededRemoteData } from '../shared/operators';
import { CoreState } from '../core-state.model';
import { FindListOptions } from '../data/find-list-options.model'; import { FindListOptions } from '../data/find-list-options.model';
/** /**
@@ -28,35 +23,21 @@ import { FindListOptions } from '../data/find-list-options.model';
@dataService(CLAIMED_TASK) @dataService(CLAIMED_TASK)
export class ClaimedTaskDataService extends TasksService<ClaimedTask> { export class ClaimedTaskDataService extends TasksService<ClaimedTask> {
protected responseMsToLive = 1000;
/**
* The endpoint link name
*/
protected linkPath = 'claimedtasks';
/** /**
* Initialize instance variables * Initialize instance variables
* *
* @param {RequestService} requestService * @param {RequestService} requestService
* @param {RemoteDataBuildService} rdbService * @param {RemoteDataBuildService} rdbService
* @param {Store<CoreState>} store
* @param {ObjectCacheService} objectCache * @param {ObjectCacheService} objectCache
* @param {HALEndpointService} halService * @param {HALEndpointService} halService
* @param {NotificationsService} notificationsService
* @param {HttpClient} http
* @param {DSOChangeAnalyzer<ClaimedTask} comparator
*/ */
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, ) {
protected http: HttpClient, super('claimedtasks', requestService, rdbService, objectCache, halService, 1000);
protected comparator: DSOChangeAnalyzer<ClaimedTask>) {
super();
} }
/** /**

View File

@@ -1,11 +1,8 @@
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { HttpHeaders } from '@angular/common/http';
import { Store } from '@ngrx/store';
import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { getMockRequestService } from '../../shared/mocks/request.service.mock';
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { PoolTaskDataService } from './pool-task-data.service'; import { PoolTaskDataService } from './pool-task-data.service';
import { getTestScheduler } from 'jasmine-marbles'; import { getTestScheduler } from 'jasmine-marbles';
import { TestScheduler } from 'rxjs/testing'; import { TestScheduler } from 'rxjs/testing';
@@ -13,7 +10,6 @@ import { of as observableOf } from 'rxjs';
import { RequestParam } from '../cache/models/request-param.model'; import { RequestParam } from '../cache/models/request-param.model';
import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { HttpOptions } from '../dspace-rest/dspace-rest.service';
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
import { CoreState } from '../core-state.model';
import { FindListOptions } from '../data/find-list-options.model'; import { FindListOptions } from '../data/find-list-options.model';
describe('PoolTaskDataService', () => { describe('PoolTaskDataService', () => {
@@ -21,13 +17,9 @@ describe('PoolTaskDataService', () => {
let service: PoolTaskDataService; let service: PoolTaskDataService;
let options: HttpOptions; let options: HttpOptions;
const taskEndpoint = 'https://rest.api/task'; const taskEndpoint = 'https://rest.api/task';
const linkPath = 'pooltasks';
const requestService = getMockRequestService(); const requestService = getMockRequestService();
const halService: any = new HALEndpointServiceStub(taskEndpoint); const halService: any = new HALEndpointServiceStub(taskEndpoint);
const rdbService = {} as RemoteDataBuildService; const rdbService = {} as RemoteDataBuildService;
const notificationsService = {} as NotificationsService;
const http = {} as HttpClient;
const comparator = {} as any;
const objectCache = { const objectCache = {
addPatch: () => { addPatch: () => {
/* empty */ /* empty */
@@ -36,18 +28,13 @@ describe('PoolTaskDataService', () => {
/* empty */ /* empty */
} }
} as any; } as any;
const store = {} as Store<CoreState>;
function initTestService(): PoolTaskDataService { function initTestService(): PoolTaskDataService {
return new PoolTaskDataService( return new PoolTaskDataService(
requestService, requestService,
rdbService, rdbService,
store,
objectCache, objectCache,
halService, halService,
notificationsService,
http,
comparator
); );
} }

View File

@@ -1,13 +1,9 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { PoolTask } from './models/pool-task-object.model'; import { PoolTask } from './models/pool-task-object.model';
@@ -16,7 +12,6 @@ import { TasksService } from './tasks.service';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
import { RequestParam } from '../cache/models/request-param.model'; import { RequestParam } from '../cache/models/request-param.model';
import { getFirstCompletedRemoteData } from '../shared/operators'; import { getFirstCompletedRemoteData } from '../shared/operators';
import { CoreState } from '../core-state.model';
import { FindListOptions } from '../data/find-list-options.model'; import { FindListOptions } from '../data/find-list-options.model';
/** /**
@@ -25,37 +20,21 @@ import { FindListOptions } from '../data/find-list-options.model';
@Injectable() @Injectable()
@dataService(POOL_TASK) @dataService(POOL_TASK)
export class PoolTaskDataService extends TasksService<PoolTask> { export class PoolTaskDataService extends TasksService<PoolTask> {
/**
* The endpoint link name
*/
protected linkPath = 'pooltasks';
protected responseMsToLive = 1000;
/** /**
* Initialize instance variables * Initialize instance variables
* *
* @param {RequestService} requestService * @param {RequestService} requestService
* @param {RemoteDataBuildService} rdbService * @param {RemoteDataBuildService} rdbService
* @param {NormalizedObjectBuildService} linkService
* @param {Store<CoreState>} store
* @param {ObjectCacheService} objectCache * @param {ObjectCacheService} objectCache
* @param {HALEndpointService} halService * @param {HALEndpointService} halService
* @param {NotificationsService} notificationsService
* @param {HttpClient} http
* @param {DSOChangeAnalyzer<ClaimedTask} comparator
*/ */
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, ) {
protected http: HttpClient, super('pooltasks', requestService, rdbService, objectCache, halService, 1000);
protected comparator: DSOChangeAnalyzer<PoolTask>) {
super();
} }
/** /**

Some files were not shown because too many files have changed in this diff Show More