mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
refactor, clean up
This commit is contained in:
@@ -1,84 +1,85 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { Operation } from 'fast-json-patch';
|
import { Operation } from 'fast-json-patch';
|
||||||
import { AsyncSubject, combineLatest, from as observableFrom, Observable, of as observableOf } from 'rxjs';
|
import { AsyncSubject, from as observableFrom, Observable } from 'rxjs';
|
||||||
import {
|
import {
|
||||||
distinctUntilChanged,
|
|
||||||
filter,
|
|
||||||
find,
|
find,
|
||||||
map,
|
map,
|
||||||
mergeMap,
|
mergeMap,
|
||||||
skipWhile,
|
|
||||||
switchMap,
|
switchMap,
|
||||||
take,
|
take,
|
||||||
takeWhile,
|
|
||||||
tap,
|
|
||||||
toArray
|
toArray
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
import { hasValue, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
|
import { hasValue } from '../../shared/empty.util';
|
||||||
import { NotificationOptions } from '../../shared/notifications/models/notification-options.model';
|
|
||||||
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 { getClassForType } 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 { ObjectCacheEntry } from '../cache/object-cache.reducer';
|
import { ObjectCacheEntry } from '../cache/object-cache.reducer';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { DSpaceSerializer } from '../dspace-rest/dspace.serializer';
|
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { getFirstCompletedRemoteData, getFirstSucceededRemoteData, getRemoteDataPayload } from '../shared/operators';
|
|
||||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
|
||||||
import { ChangeAnalyzer } from './change-analyzer';
|
import { ChangeAnalyzer } from './change-analyzer';
|
||||||
import { PaginatedList } from './paginated-list.model';
|
import { PaginatedList } from './paginated-list.model';
|
||||||
import { RemoteData } from './remote-data';
|
import { RemoteData } from './remote-data';
|
||||||
import {
|
import {
|
||||||
CreateRequest,
|
|
||||||
DeleteByIDRequest,
|
DeleteByIDRequest,
|
||||||
DeleteRequest,
|
PostRequest
|
||||||
GetRequest,
|
|
||||||
PatchRequest,
|
|
||||||
PostRequest,
|
|
||||||
PutRequest
|
|
||||||
} from './request.models';
|
} from './request.models';
|
||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
import { RestRequestMethod } from './rest-request-method';
|
import { RestRequestMethod } from './rest-request-method';
|
||||||
import { GenericConstructor } from '../shared/generic-constructor';
|
|
||||||
import { NoContent } from '../shared/NoContent.model';
|
import { NoContent } from '../shared/NoContent.model';
|
||||||
import { CacheableObject } from '../cache/cacheable-object.model';
|
import { CacheableObject } from '../cache/cacheable-object.model';
|
||||||
import { CoreState } from '../core-state.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";
|
import { FindAllData, FindAllDataImpl } from './base/find-all-data';
|
||||||
import { FindAllData, FindAllDataImpl } from "./base/find-all-data";
|
import { SearchData, SearchDataImpl } from './base/search-data';
|
||||||
import { SearchData, SearchDataImpl } from "./base/search-data";
|
import { CreateData, CreateDataImpl } from './base/create-data';
|
||||||
import { CreateData, CreateDataImpl } from "./base/create-data";
|
import { PatchData, PatchDataImpl } from './base/patch-data';
|
||||||
|
import { IdentifiableDataService } from './base/identifiable-data.service';
|
||||||
|
import { PutData, PutDataImpl } from './base/put-data';
|
||||||
|
import { DeleteData, DeleteDataImpl } from './base/delete-data';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to list the methods used by the injected service in components
|
||||||
|
*/
|
||||||
export interface UpdateDataService<T> {
|
export interface UpdateDataService<T> {
|
||||||
patch(dso: T, operations: Operation[]): Observable<RemoteData<T>>;
|
patch(dso: T, operations: Operation[]): Observable<RemoteData<T>>;
|
||||||
update(object: T): Observable<RemoteData<T>>;
|
update(object: T): Observable<RemoteData<T>>;
|
||||||
commitUpdates(method?: RestRequestMethod);
|
commitUpdates(method?: RestRequestMethod): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specific functionalities that not all services would need.
|
* Specific functionalities that not all services would need.
|
||||||
* Goal of the class is to update remote objects, handling custom methods that don't belong to BaseDataService
|
* Goal of the class is to update remote objects, handling custom methods that don't belong to BaseDataService
|
||||||
|
* The class implements also the following common interfaces
|
||||||
|
*
|
||||||
|
* findAllData: FindAllData<T>;
|
||||||
|
* searchData: SearchData<T>;
|
||||||
|
* createData: CreateData<T>;
|
||||||
|
* patchData: PatchData<T>;
|
||||||
|
* putData: PutData<T>;
|
||||||
|
* deleteData: DeleteData<T>;
|
||||||
*
|
*
|
||||||
* Custom methods are:
|
* Custom methods are:
|
||||||
*
|
*
|
||||||
* patch - Sends a patch request to modify current object
|
* deleteOnRelated - delete all related objects to the given one
|
||||||
* update - Add a new patch to the object cache, diff between given object and cache
|
* postOnRelated - post all the related objects to the given one
|
||||||
* commitUpdates - Sends the updates to the server
|
* invalidate - invalidate the DSpaceObject making all requests as stale
|
||||||
|
* invalidateByHref - invalidate the href making all requests as stale
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export abstract class UpdateDataServiceImpl<T extends CacheableObject> extends IdentifiableDataService<T> implements FindAllData<T>, SearchData<T>, CreateData<T>, PatchData<T>, PutData<T>, DeleteData<T> {
|
||||||
export abstract class UpdateDataServiceImpl<T extends CacheableObject> extends BaseDataService<T> implements UpdateDataService<T>, FindAllData<T>, SearchData<T>, CreateData<T> {
|
|
||||||
protected abstract store: Store<CoreState>;
|
protected abstract store: Store<CoreState>;
|
||||||
protected abstract http: HttpClient;
|
protected abstract http: HttpClient;
|
||||||
protected abstract comparator: ChangeAnalyzer<T>;
|
|
||||||
|
|
||||||
private findAllData: FindAllDataImpl<T>;
|
private findAllData: FindAllDataImpl<T>;
|
||||||
private searchData: SearchDataImpl<T>;
|
private searchData: SearchDataImpl<T>;
|
||||||
private createData: CreateData<T>;
|
private createData: CreateDataImpl<T>;
|
||||||
|
private patchData: PatchDataImpl<T>;
|
||||||
|
private putData: PutDataImpl<T>;
|
||||||
|
private deleteData: DeleteDataImpl<T>;
|
||||||
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -88,12 +89,16 @@ export abstract class UpdateDataServiceImpl<T extends CacheableObject> extends B
|
|||||||
protected objectCache: ObjectCacheService,
|
protected objectCache: ObjectCacheService,
|
||||||
protected halService: HALEndpointService,
|
protected halService: HALEndpointService,
|
||||||
protected notificationsService: NotificationsService,
|
protected notificationsService: NotificationsService,
|
||||||
|
protected comparator: ChangeAnalyzer<T>,
|
||||||
protected responseMsToLive: number,
|
protected responseMsToLive: number,
|
||||||
) {
|
) {
|
||||||
super(linkPath, requestService, rdbService, objectCache, halService, responseMsToLive);
|
super(linkPath, requestService, rdbService, objectCache, halService, responseMsToLive);
|
||||||
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
||||||
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
||||||
this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService ,this.responseMsToLive);
|
this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService ,this.responseMsToLive);
|
||||||
|
this.patchData = new PatchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, comparator ,this.responseMsToLive, this.constructIdEndpoint);
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -107,7 +112,7 @@ export abstract class UpdateDataServiceImpl<T extends CacheableObject> extends B
|
|||||||
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
|
* @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> {
|
public getFindAllHref(options: FindListOptions = {}, linkPath?: string, ...linksToFollow: FollowLinkConfig<T>[]): Observable<string> {
|
||||||
return this.findAllData.getFindAllHref(options, linkPath, ...linksToFollow)
|
return this.findAllData.getFindAllHref(options, linkPath, ...linksToFollow);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -120,7 +125,7 @@ export abstract class UpdateDataServiceImpl<T extends CacheableObject> extends B
|
|||||||
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
|
* @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> {
|
public getSearchByHref(searchMethod: string, options: FindListOptions = {}, ...linksToFollow: FollowLinkConfig<T>[]): Observable<string> {
|
||||||
return this.searchData.getSearchByHref(searchMethod, options, ...linksToFollow)
|
return this.searchData.getSearchByHref(searchMethod, options, ...linksToFollow);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -138,27 +143,7 @@ export abstract class UpdateDataServiceImpl<T extends CacheableObject> extends B
|
|||||||
* Return an observable that emits object list
|
* Return an observable that emits object list
|
||||||
*/
|
*/
|
||||||
findAll(options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<T>[]): Observable<RemoteData<PaginatedList<T>>> {
|
findAll(options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<T>[]): Observable<RemoteData<PaginatedList<T>>> {
|
||||||
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)
|
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the HREF for a specific object based on its identifier; with possible embed query params based on linksToFollow
|
|
||||||
* @param endpoint The base endpoint for the type of object
|
|
||||||
* @param resourceID The identifier for the object
|
|
||||||
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
|
|
||||||
*/
|
|
||||||
getIDHref(endpoint, resourceID, ...linksToFollow: FollowLinkConfig<T>[]): string {
|
|
||||||
return this.buildHrefFromFindOptions(endpoint + '/' + resourceID, {}, [], ...linksToFollow);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an observable for the HREF of a specific object based on its identifier
|
|
||||||
* @param resourceID The identifier for the object
|
|
||||||
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
|
|
||||||
*/
|
|
||||||
getIDHrefObs(resourceID: string, ...linksToFollow: FollowLinkConfig<T>[]): Observable<string> {
|
|
||||||
return this.getEndpoint().pipe(
|
|
||||||
map((endpoint: string) => this.getIDHref(endpoint, resourceID, ...linksToFollow)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -177,29 +162,6 @@ export abstract class UpdateDataServiceImpl<T extends CacheableObject> extends B
|
|||||||
return this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
return this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* An operator that will call the given function if the incoming RemoteData is stale and
|
|
||||||
* shouldReRequest is true
|
|
||||||
*
|
|
||||||
* @param shouldReRequest Whether or not to call the re-request function if the RemoteData is stale
|
|
||||||
* @param requestFn The function to call if the RemoteData is stale and shouldReRequest is
|
|
||||||
* true
|
|
||||||
*/
|
|
||||||
protected reRequestStaleRemoteData<O>(shouldReRequest: boolean, requestFn: () => Observable<RemoteData<O>>) {
|
|
||||||
return (source: Observable<RemoteData<O>>): Observable<RemoteData<O>> => {
|
|
||||||
if (shouldReRequest === true) {
|
|
||||||
return source.pipe(
|
|
||||||
tap((remoteData: RemoteData<O>) => {
|
|
||||||
if (hasValue(remoteData) && remoteData.isStale) {
|
|
||||||
requestFn();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a new FindListRequest with given search method
|
* Make a new FindListRequest with given search method
|
||||||
@@ -216,7 +178,7 @@ export abstract class UpdateDataServiceImpl<T extends CacheableObject> extends B
|
|||||||
* Return an observable that emits response from the server
|
* Return an observable that emits response from the server
|
||||||
*/
|
*/
|
||||||
searchBy(searchMethod: string, options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<T>[]): Observable<RemoteData<PaginatedList<T>>> {
|
searchBy(searchMethod: string, options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<T>[]): Observable<RemoteData<PaginatedList<T>>> {
|
||||||
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)
|
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -225,30 +187,11 @@ export abstract class UpdateDataServiceImpl<T extends CacheableObject> extends B
|
|||||||
* @param {Operation[]} operations The patch operations to be performed
|
* @param {Operation[]} operations The patch operations to be performed
|
||||||
*/
|
*/
|
||||||
patch(object: T, operations: Operation[]): Observable<RemoteData<T>> {
|
patch(object: T, operations: Operation[]): Observable<RemoteData<T>> {
|
||||||
const requestId = this.requestService.generateRequestId();
|
return this.patchData.patch(object, operations);
|
||||||
|
|
||||||
const hrefObs = this.halService.getEndpoint(this.linkPath).pipe(
|
|
||||||
map((endpoint: string) => this.getIDHref(endpoint, object.uuid)));
|
|
||||||
|
|
||||||
hrefObs.pipe(
|
|
||||||
find((href: string) => hasValue(href)),
|
|
||||||
).subscribe((href: string) => {
|
|
||||||
const request = new PatchRequest(requestId, href, operations);
|
|
||||||
if (hasValue(this.responseMsToLive)) {
|
|
||||||
request.responseMsToLive = this.responseMsToLive;
|
|
||||||
}
|
|
||||||
this.requestService.send(request);
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.rdbService.buildFromRequestUUID(requestId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createPatchFromCache(object: T): Observable<Operation[]> {
|
createPatchFromCache(object: T): Observable<Operation[]> {
|
||||||
const oldVersion$ = this.findByHref(object._links.self.href, true, false);
|
return this.patchData.createPatchFromCache(object);
|
||||||
return oldVersion$.pipe(
|
|
||||||
getFirstSucceededRemoteData(),
|
|
||||||
getRemoteDataPayload(),
|
|
||||||
map((oldVersion: T) => this.comparator.diff(oldVersion, object)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -257,17 +200,7 @@ export abstract class UpdateDataServiceImpl<T extends CacheableObject> extends B
|
|||||||
* @param object The object to send a put request for.
|
* @param object The object to send a put request for.
|
||||||
*/
|
*/
|
||||||
put(object: T): Observable<RemoteData<T>> {
|
put(object: T): Observable<RemoteData<T>> {
|
||||||
const requestId = this.requestService.generateRequestId();
|
return this.putData.put(object);
|
||||||
const serializedObject = new DSpaceSerializer(object.constructor as GenericConstructor<{}>).serialize(object);
|
|
||||||
const request = new PutRequest(requestId, object._links.self.href, serializedObject);
|
|
||||||
|
|
||||||
if (hasValue(this.responseMsToLive)) {
|
|
||||||
request.responseMsToLive = this.responseMsToLive;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.requestService.send(request);
|
|
||||||
|
|
||||||
return this.rdbService.buildFromRequestUUID(requestId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -276,16 +209,7 @@ export abstract class UpdateDataServiceImpl<T extends CacheableObject> extends B
|
|||||||
* @param {DSpaceObject} object The given object
|
* @param {DSpaceObject} object The given object
|
||||||
*/
|
*/
|
||||||
update(object: T): Observable<RemoteData<T>> {
|
update(object: T): Observable<RemoteData<T>> {
|
||||||
return this.createPatchFromCache(object)
|
return this.patchData.update(object);
|
||||||
.pipe(
|
|
||||||
mergeMap((operations: Operation[]) => {
|
|
||||||
if (isNotEmpty(operations)) {
|
|
||||||
this.objectCache.addPatch(object._links.self.href, operations);
|
|
||||||
}
|
|
||||||
return this.findByHref(object._links.self.href, true, true);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -298,7 +222,7 @@ export abstract class UpdateDataServiceImpl<T extends CacheableObject> extends B
|
|||||||
* Array with additional params to combine with query string
|
* Array with additional params to combine with query string
|
||||||
*/
|
*/
|
||||||
create(object: T, ...params: RequestParam[]): Observable<RemoteData<T>> {
|
create(object: T, ...params: RequestParam[]): Observable<RemoteData<T>> {
|
||||||
return this.createData.create(object, ...params)
|
return this.createData.create(object, ...params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -390,9 +314,7 @@ export abstract class UpdateDataServiceImpl<T extends CacheableObject> extends B
|
|||||||
* errorMessage, timeCompleted, etc
|
* errorMessage, timeCompleted, etc
|
||||||
*/
|
*/
|
||||||
delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
|
delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
|
||||||
return this.getIDHrefObs(objectId).pipe(
|
return this.deleteData.delete(objectId, copyVirtualMetadata);
|
||||||
switchMap((href: string) => this.deleteByHref(href, copyVirtualMetadata))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -405,43 +327,7 @@ export abstract class UpdateDataServiceImpl<T extends CacheableObject> extends B
|
|||||||
* Only emits once all request related to the DSO has been invalidated.
|
* Only emits once all request related to the DSO has been invalidated.
|
||||||
*/
|
*/
|
||||||
deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
|
deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
|
||||||
const requestId = this.requestService.generateRequestId();
|
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
|
||||||
|
|
||||||
if (copyVirtualMetadata) {
|
|
||||||
copyVirtualMetadata.forEach((id) =>
|
|
||||||
href += (href.includes('?') ? '&' : '?')
|
|
||||||
+ 'copyVirtualMetadata='
|
|
||||||
+ id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = new DeleteRequest(requestId, href);
|
|
||||||
if (hasValue(this.responseMsToLive)) {
|
|
||||||
request.responseMsToLive = this.responseMsToLive;
|
|
||||||
}
|
|
||||||
this.requestService.send(request);
|
|
||||||
|
|
||||||
const response$ = this.rdbService.buildFromRequestUUID(requestId);
|
|
||||||
|
|
||||||
const invalidated$ = new AsyncSubject<boolean>();
|
|
||||||
response$.pipe(
|
|
||||||
getFirstCompletedRemoteData(),
|
|
||||||
switchMap((rd: RemoteData<NoContent>) => {
|
|
||||||
if (rd.hasSucceeded) {
|
|
||||||
return this.invalidateByHref(href);
|
|
||||||
} else {
|
|
||||||
return [true];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
).subscribe(() => {
|
|
||||||
invalidated$.next(true);
|
|
||||||
invalidated$.complete();
|
|
||||||
});
|
|
||||||
|
|
||||||
return combineLatest([response$, invalidated$]).pipe(
|
|
||||||
filter(([_, invalidated]) => invalidated),
|
|
||||||
map(([response, _]) => response),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -449,6 +335,6 @@ export abstract class UpdateDataServiceImpl<T extends CacheableObject> extends B
|
|||||||
* @param method The RestRequestMethod for which de server sync buffer should be committed
|
* @param method The RestRequestMethod for which de server sync buffer should be committed
|
||||||
*/
|
*/
|
||||||
commitUpdates(method?: RestRequestMethod) {
|
commitUpdates(method?: RestRequestMethod) {
|
||||||
this.requestService.commit(method);
|
this.patchData.commitUpdates(method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -58,7 +58,7 @@ export class SuggestionDataServiceImpl extends UpdateDataServiceImpl<Suggestion>
|
|||||||
protected comparator: ChangeAnalyzer<Suggestion>,
|
protected comparator: ChangeAnalyzer<Suggestion>,
|
||||||
protected responseMsToLive: number,
|
protected responseMsToLive: number,
|
||||||
) {
|
) {
|
||||||
super('suggestions', requestService, rdbService, objectCache, halService, notificationsService ,responseMsToLive);
|
super('suggestions', requestService, rdbService, objectCache, halService, notificationsService, comparator ,responseMsToLive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +99,6 @@ export class SuggestionsDataService {
|
|||||||
* @param {DefaultChangeAnalyzer<Suggestion>} comparatorSuggestions
|
* @param {DefaultChangeAnalyzer<Suggestion>} comparatorSuggestions
|
||||||
* @param {DefaultChangeAnalyzer<SuggestionSource>} comparatorSources
|
* @param {DefaultChangeAnalyzer<SuggestionSource>} comparatorSources
|
||||||
* @param {DefaultChangeAnalyzer<SuggestionTarget>} comparatorTargets
|
* @param {DefaultChangeAnalyzer<SuggestionTarget>} comparatorTargets
|
||||||
* @param responseMsToLive
|
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
|
@@ -21,7 +21,7 @@ import { ArrayMoveChangeAnalyzer } from '../../core/data/array-move-change-analy
|
|||||||
import { DATA_SERVICE_FACTORY } from '../../core/data/base/data-service.decorator';
|
import { DATA_SERVICE_FACTORY } from '../../core/data/base/data-service.decorator';
|
||||||
import { GenericConstructor } from '../../core/shared/generic-constructor';
|
import { GenericConstructor } from '../../core/shared/generic-constructor';
|
||||||
import { HALDataService } from '../../core/data/base/hal-data-service.interface';
|
import { HALDataService } from '../../core/data/base/hal-data-service.interface';
|
||||||
import { UpdateDataService } from "../../core/data/update-data-service";
|
import { UpdateDataService } from '../../core/data/update-data-service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-dso-edit-metadata',
|
selector: 'ds-dso-edit-metadata',
|
||||||
|
@@ -2,7 +2,7 @@ import { ThemedComponent } from '../../shared/theme-support/themed.component';
|
|||||||
import { DsoEditMetadataComponent } from './dso-edit-metadata.component';
|
import { DsoEditMetadataComponent } from './dso-edit-metadata.component';
|
||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||||
import { UpdateDataService } from "../../core/data/update-data-service";
|
import { UpdateDataService } from '../../core/data/update-data-service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-themed-dso-edit-metadata',
|
selector: 'ds-themed-dso-edit-metadata',
|
||||||
|
@@ -541,7 +541,7 @@ export class MenuResolver implements Resolve<boolean> {
|
|||||||
{
|
{
|
||||||
id: 'notifications',
|
id: 'notifications',
|
||||||
active: false,
|
active: false,
|
||||||
visible: authorized && true,
|
visible: authorized && canSeeQA,
|
||||||
model: {
|
model: {
|
||||||
type: MenuItemType.TEXT,
|
type: MenuItemType.TEXT,
|
||||||
text: 'menu.section.notifications'
|
text: 'menu.section.notifications'
|
||||||
|
@@ -86,10 +86,10 @@ export class SuggestionActionsComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ignoreSuggestionLabel(): string {
|
ignoreSuggestionLabel(): string {
|
||||||
return this.isBulk ? 'reciter.suggestion.ignoreSuggestion.bulk' : 'reciter.suggestion.ignoreSuggestion' ;
|
return this.isBulk ? 'suggestion.ignoreSuggestion.bulk' : 'suggestion.ignoreSuggestion' ;
|
||||||
}
|
}
|
||||||
|
|
||||||
approveAndImportLabel(): string {
|
approveAndImportLabel(): string {
|
||||||
return this.isBulk ? 'reciter.suggestion.approveAndImport.bulk' : 'reciter.suggestion.approveAndImport';
|
return this.isBulk ? 'suggestion.approveAndImport.bulk' : 'suggestion.approveAndImport';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -55,7 +55,7 @@ export class SuggestionTargetsEffects {
|
|||||||
retrieveAllTargetsErrorAction$ = createEffect(() => this.actions$.pipe(
|
retrieveAllTargetsErrorAction$ = createEffect(() => this.actions$.pipe(
|
||||||
ofType(SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE_ERROR),
|
ofType(SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE_ERROR),
|
||||||
tap(() => {
|
tap(() => {
|
||||||
this.notificationsService.error(null, this.translate.get('reciter.suggestion.target.error.service.retrieve'));
|
this.notificationsService.error(null, this.translate.get('suggestion.target.error.service.retrieve'));
|
||||||
})
|
})
|
||||||
), { dispatch: false });
|
), { dispatch: false });
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ export class SuggestionTargetsEffects {
|
|||||||
*/
|
*/
|
||||||
refreshUserSuggestionsAction$ = createEffect(() => this.actions$.pipe(
|
refreshUserSuggestionsAction$ = createEffect(() => this.actions$.pipe(
|
||||||
ofType(SuggestionTargetActionTypes.REFRESH_USER_SUGGESTIONS),
|
ofType(SuggestionTargetActionTypes.REFRESH_USER_SUGGESTIONS),
|
||||||
switchMap((action: RefreshUserSuggestionsAction) => {
|
switchMap(() => {
|
||||||
return this.store$.select((state: any) => state.core.auth.userId)
|
return this.store$.select((state: any) => state.core.auth.userId)
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((userId: string) => {
|
switchMap((userId: string) => {
|
||||||
|
@@ -169,18 +169,18 @@ describe('SuggestionsService test', () => {
|
|||||||
it('should get suggestion interpolation', () => {
|
it('should get suggestion interpolation', () => {
|
||||||
const result = service.getNotificationSuggestionInterpolation(suggestionTarget as SuggestionTarget);
|
const result = service.getNotificationSuggestionInterpolation(suggestionTarget as SuggestionTarget);
|
||||||
expect(result.count).toEqual(suggestionTarget.total);
|
expect(result.count).toEqual(suggestionTarget.total);
|
||||||
expect(result.source).toEqual('reciter.suggestion.source.' + suggestionTarget.source);
|
expect(result.source).toEqual('suggestion.source.' + suggestionTarget.source);
|
||||||
expect(result.type).toEqual('reciter.suggestion.type.' + suggestionTarget.source);
|
expect(result.type).toEqual('suggestion.type.' + suggestionTarget.source);
|
||||||
expect(result.suggestionId).toEqual(suggestionTarget.id);
|
expect(result.suggestionId).toEqual(suggestionTarget.id);
|
||||||
expect(result.displayName).toEqual(suggestionTarget.display);
|
expect(result.displayName).toEqual(suggestionTarget.display);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should translate suggestion type', () => {
|
it('should translate suggestion type', () => {
|
||||||
expect(service.translateSuggestionType('source')).toEqual('reciter.suggestion.type.source');
|
expect(service.translateSuggestionType('source')).toEqual('suggestion.type.source');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should translate suggestion source', () => {
|
it('should translate suggestion source', () => {
|
||||||
expect(service.translateSuggestionSource('source')).toEqual('reciter.suggestion.source.source');
|
expect(service.translateSuggestionSource('source')).toEqual('suggestion.source.source');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should resolve collection id', () => {
|
it('should resolve collection id', () => {
|
||||||
|
@@ -51,17 +51,14 @@ export class SuggestionsService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the service variables.
|
* Initialize the service variables.
|
||||||
* @param {AuthService} authService
|
|
||||||
* @param {ResearcherProfileDataService} researcherProfileService
|
* @param {ResearcherProfileDataService} researcherProfileService
|
||||||
* @param {SuggestionSourceDataService} suggestionSourceDataService
|
|
||||||
* @param {SuggestionTargetDataService} suggestionTargetDataService
|
* @param {SuggestionTargetDataService} suggestionTargetDataService
|
||||||
* @param {SuggestionsDataService} suggestionsDataService
|
* @param {SuggestionsDataService} suggestionsDataService
|
||||||
|
* @param translateService
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
private authService: AuthService,
|
|
||||||
private researcherProfileService: ResearcherProfileDataService,
|
private researcherProfileService: ResearcherProfileDataService,
|
||||||
private suggestionsDataService: SuggestionsDataService,
|
private suggestionsDataService: SuggestionsDataService,
|
||||||
private suggestionSourceDataService: SuggestionSourceDataService,
|
|
||||||
private suggestionTargetDataService: SuggestionTargetDataService,
|
private suggestionTargetDataService: SuggestionTargetDataService,
|
||||||
private translateService: TranslateService
|
private translateService: TranslateService
|
||||||
) {
|
) {
|
||||||
@@ -194,7 +191,7 @@ export class SuggestionsService {
|
|||||||
return workspaceitemService.importExternalSourceEntry(suggestion.externalSourceUri, resolvedCollectionId)
|
return workspaceitemService.importExternalSourceEntry(suggestion.externalSourceUri, resolvedCollectionId)
|
||||||
.pipe(
|
.pipe(
|
||||||
getFirstSucceededRemoteDataPayload(),
|
getFirstSucceededRemoteDataPayload(),
|
||||||
catchError((error) => of(null))
|
catchError(() => of(null))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +201,7 @@ export class SuggestionsService {
|
|||||||
*/
|
*/
|
||||||
public ignoreSuggestion(suggestionId): Observable<RemoteData<NoContent>> {
|
public ignoreSuggestion(suggestionId): Observable<RemoteData<NoContent>> {
|
||||||
return this.deleteReviewedSuggestion(suggestionId).pipe(
|
return this.deleteReviewedSuggestion(suggestionId).pipe(
|
||||||
catchError((error) => of(null))
|
catchError(() => of(null))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,11 +265,11 @@ export class SuggestionsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public translateSuggestionType(source: string): string {
|
public translateSuggestionType(source: string): string {
|
||||||
return 'reciter.suggestion.type.' + source;
|
return 'suggestion.type.' + source;
|
||||||
}
|
}
|
||||||
|
|
||||||
public translateSuggestionSource(source: string): string {
|
public translateSuggestionSource(source: string): string {
|
||||||
return 'reciter.suggestion.source.' + source;
|
return 'suggestion.source.' + source;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -13,8 +13,6 @@ import {
|
|||||||
} from '../suggestion-notifications/suggestion-list-element/suggestion-list-element.component';
|
} from '../suggestion-notifications/suggestion-list-element/suggestion-list-element.component';
|
||||||
import { SuggestionsService } from '../suggestion-notifications/suggestions.service';
|
import { SuggestionsService } from '../suggestion-notifications/suggestions.service';
|
||||||
import { getMockSuggestionNotificationsStateService, getMockSuggestionsService } from '../shared/mocks/suggestion.mock';
|
import { getMockSuggestionNotificationsStateService, getMockSuggestionsService } from '../shared/mocks/suggestion.mock';
|
||||||
import { buildPaginatedList, PaginatedList } from '../core/data/paginated-list.model';
|
|
||||||
import { Suggestion } from '../core/suggestion-notifications/models/suggestion.model';
|
|
||||||
import { mockSuggestionPublicationOne, mockSuggestionPublicationTwo } from '../shared/mocks/publication-claim.mock';
|
import { mockSuggestionPublicationOne, mockSuggestionPublicationTwo } from '../shared/mocks/publication-claim.mock';
|
||||||
import { SuggestionEvidencesComponent } from '../suggestion-notifications/suggestion-list-element/suggestion-evidences/suggestion-evidences.component';
|
import { SuggestionEvidencesComponent } from '../suggestion-notifications/suggestion-list-element/suggestion-evidences/suggestion-evidences.component';
|
||||||
import { ObjectKeysPipe } from '../shared/utils/object-keys-pipe';
|
import { ObjectKeysPipe } from '../shared/utils/object-keys-pipe';
|
||||||
@@ -29,7 +27,6 @@ import { getMockTranslateService } from '../shared/mocks/translate.service.mock'
|
|||||||
import { SuggestionTargetsStateService } from '../suggestion-notifications/suggestion-targets/suggestion-targets.state.service';
|
import { SuggestionTargetsStateService } from '../suggestion-notifications/suggestion-targets/suggestion-targets.state.service';
|
||||||
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
|
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
|
||||||
import { createSuccessfulRemoteDataObject } from '../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject } from '../shared/remote-data.utils';
|
||||||
import { PageInfo } from '../core/shared/page-info.model';
|
|
||||||
import { TestScheduler } from 'rxjs/testing';
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
import { getTestScheduler } from 'jasmine-marbles';
|
import { getTestScheduler } from 'jasmine-marbles';
|
||||||
import { PaginationServiceStub } from '../shared/testing/pagination-service.stub';
|
import { PaginationServiceStub } from '../shared/testing/pagination-service.stub';
|
||||||
|
@@ -152,7 +152,7 @@ export class SuggestionsPageComponent implements OnInit {
|
|||||||
* @suggestionId
|
* @suggestionId
|
||||||
*/
|
*/
|
||||||
ignoreSuggestion(suggestionId) {
|
ignoreSuggestion(suggestionId) {
|
||||||
this.suggestionService.ignoreSuggestion(suggestionId).subscribe((res) => {
|
this.suggestionService.ignoreSuggestion(suggestionId).subscribe(() => {
|
||||||
this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction();
|
this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction();
|
||||||
this.updatePage();
|
this.updatePage();
|
||||||
});
|
});
|
||||||
@@ -172,12 +172,12 @@ export class SuggestionsPageComponent implements OnInit {
|
|||||||
this.selectedSuggestions = {};
|
this.selectedSuggestions = {};
|
||||||
if (results.success > 0) {
|
if (results.success > 0) {
|
||||||
this.notificationService.success(
|
this.notificationService.success(
|
||||||
this.translateService.get('reciter.suggestion.notMine.bulk.success',
|
this.translateService.get('suggestion.notMine.bulk.success',
|
||||||
{count: results.success}));
|
{count: results.success}));
|
||||||
}
|
}
|
||||||
if (results.fails > 0) {
|
if (results.fails > 0) {
|
||||||
this.notificationService.error(
|
this.notificationService.error(
|
||||||
this.translateService.get('reciter.suggestion.notMine.bulk.error',
|
this.translateService.get('suggestion.notMine.bulk.error',
|
||||||
{count: results.fails}));
|
{count: results.fails}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -190,7 +190,7 @@ export class SuggestionsPageComponent implements OnInit {
|
|||||||
approveAndImport(event: SuggestionApproveAndImport) {
|
approveAndImport(event: SuggestionApproveAndImport) {
|
||||||
this.suggestionService.approveAndImport(this.workspaceItemService, event.suggestion, event.collectionId)
|
this.suggestionService.approveAndImport(this.workspaceItemService, event.suggestion, event.collectionId)
|
||||||
.subscribe((workspaceitem: WorkspaceItem) => {
|
.subscribe((workspaceitem: WorkspaceItem) => {
|
||||||
const content = this.translateService.instant('reciter.suggestion.approveAndImport.success', { workspaceItemId: workspaceitem.id });
|
const content = this.translateService.instant('suggestion.approveAndImport.success', { workspaceItemId: workspaceitem.id });
|
||||||
this.notificationService.success('', content, {timeOut:0}, true);
|
this.notificationService.success('', content, {timeOut:0}, true);
|
||||||
this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction();
|
this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction();
|
||||||
this.updatePage();
|
this.updatePage();
|
||||||
@@ -212,12 +212,12 @@ export class SuggestionsPageComponent implements OnInit {
|
|||||||
this.selectedSuggestions = {};
|
this.selectedSuggestions = {};
|
||||||
if (results.success > 0) {
|
if (results.success > 0) {
|
||||||
this.notificationService.success(
|
this.notificationService.success(
|
||||||
this.translateService.get('reciter.suggestion.approveAndImport.bulk.success',
|
this.translateService.get('suggestion.approveAndImport.bulk.success',
|
||||||
{count: results.success}));
|
{count: results.success}));
|
||||||
}
|
}
|
||||||
if (results.fails > 0) {
|
if (results.fails > 0) {
|
||||||
this.notificationService.error(
|
this.notificationService.error(
|
||||||
this.translateService.get('reciter.suggestion.approveAndImport.bulk.error',
|
this.translateService.get('suggestion.approveAndImport.bulk.error',
|
||||||
{count: results.fails}));
|
{count: results.fails}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Config } from "./config.interface";
|
import { Config } from './config.interface';
|
||||||
|
|
||||||
export interface SuggestionConfig extends Config {
|
export interface SuggestionConfig extends Config {
|
||||||
source: string;
|
source: string;
|
||||||
|
Reference in New Issue
Block a user