mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-10 19:43:04 +00:00
refactor, fix tests
This commit is contained in:
@@ -1 +1 @@
|
||||
<ds-suggestion-target [source]="'openaire'"></ds-suggestion-target>
|
||||
<ds-publication-claim [source]="'openaire'"></ds-publication-claim>
|
||||
|
@@ -1,13 +1,13 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-publication-claim-page.component';
|
||||
import { AdminNotificationsPublicationClaimPageComponent } from './admin-notifications-publication-claim-page.component';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
describe('AdminNotificationsSuggestionTargetsPageComponent', () => {
|
||||
let component: AdminNotificationsSuggestionTargetsPageComponent;
|
||||
let fixture: ComponentFixture<AdminNotificationsSuggestionTargetsPageComponent>;
|
||||
describe('AdminNotificationsPublicationClaimPageComponent', () => {
|
||||
let component: AdminNotificationsPublicationClaimPageComponent;
|
||||
let fixture: ComponentFixture<AdminNotificationsPublicationClaimPageComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
@@ -16,10 +16,10 @@ describe('AdminNotificationsSuggestionTargetsPageComponent', () => {
|
||||
TranslateModule.forRoot()
|
||||
],
|
||||
declarations: [
|
||||
AdminNotificationsSuggestionTargetsPageComponent
|
||||
AdminNotificationsPublicationClaimPageComponent
|
||||
],
|
||||
providers: [
|
||||
AdminNotificationsSuggestionTargetsPageComponent
|
||||
AdminNotificationsPublicationClaimPageComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
@@ -27,7 +27,7 @@ describe('AdminNotificationsSuggestionTargetsPageComponent', () => {
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AdminNotificationsSuggestionTargetsPageComponent);
|
||||
fixture = TestBed.createComponent(AdminNotificationsPublicationClaimPageComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
@@ -5,6 +5,6 @@ import { Component } from '@angular/core';
|
||||
templateUrl: './admin-notifications-publication-claim-page.component.html',
|
||||
styleUrls: ['./admin-notifications-publication-claim-page.component.scss']
|
||||
})
|
||||
export class AdminNotificationsSuggestionTargetsPageComponent {
|
||||
export class AdminNotificationsPublicationClaimPageComponent {
|
||||
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ import { AuthenticatedGuard } from '../../core/auth/authenticated.guard';
|
||||
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||
import { I18nBreadcrumbsService } from '../../core/breadcrumbs/i18n-breadcrumbs.service';
|
||||
import { PUBLICATION_CLAIMS_PATH } from './admin-notifications-routing-paths';
|
||||
import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component';
|
||||
import { AdminNotificationsPublicationClaimPageComponent } from './admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component';
|
||||
import { AdminNotificationsPublicationClaimPageResolver } from './admin-notifications-publication-claim-page/admin-notifications-publication-claim-page-resolver.service';
|
||||
import { QUALITY_ASSURANCE_EDIT_PATH } from './admin-notifications-routing-paths';
|
||||
import { AdminQualityAssuranceTopicsPageComponent } from './admin-quality-assurance-topics-page/admin-quality-assurance-topics-page.component';
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
{
|
||||
canActivate: [ AuthenticatedGuard ],
|
||||
path: `${PUBLICATION_CLAIMS_PATH}`,
|
||||
component: AdminNotificationsSuggestionTargetsPageComponent,
|
||||
component: AdminNotificationsPublicationClaimPageComponent,
|
||||
pathMatch: 'full',
|
||||
resolve: {
|
||||
breadcrumb: I18nBreadcrumbResolver,
|
||||
|
@@ -3,7 +3,7 @@ import { NgModule } from '@angular/core';
|
||||
import { CoreModule } from '../../core/core.module';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
import { AdminNotificationsRoutingModule } from './admin-notifications-routing.module';
|
||||
import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component';
|
||||
import { AdminNotificationsPublicationClaimPageComponent } from './admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component';
|
||||
import { AdminQualityAssuranceTopicsPageComponent } from './admin-quality-assurance-topics-page/admin-quality-assurance-topics-page.component';
|
||||
import { AdminQualityAssuranceEventsPageComponent } from './admin-quality-assurance-events-page/admin-quality-assurance-events-page.component';
|
||||
import { AdminQualityAssuranceSourcePageComponent } from './admin-quality-assurance-source-page-component/admin-quality-assurance-source-page.component';
|
||||
@@ -18,7 +18,7 @@ import { NotificationsModule } from '../../notifications/notifications.module';
|
||||
NotificationsModule
|
||||
],
|
||||
declarations: [
|
||||
AdminNotificationsSuggestionTargetsPageComponent,
|
||||
AdminNotificationsPublicationClaimPageComponent,
|
||||
AdminQualityAssuranceTopicsPageComponent,
|
||||
AdminQualityAssuranceEventsPageComponent,
|
||||
AdminQualityAssuranceSourcePageComponent
|
||||
|
@@ -29,11 +29,11 @@ export const EMBED_SEPARATOR = '%2F';
|
||||
/**
|
||||
* Common functionality for data services.
|
||||
* Specific functionality that not all services would need
|
||||
* is implemented in "DataService feature" classes (e.g. {@link CreateData}
|
||||
* is implemented in "UpdateDataServiceImpl feature" classes (e.g. {@link CreateData}
|
||||
*
|
||||
* All DataService (or DataService feature) classes must
|
||||
* All UpdateDataServiceImpl (or UpdateDataServiceImpl feature) classes must
|
||||
* - extend this class (or {@link IdentifiableDataService})
|
||||
* - implement any DataService features it requires in order to forward calls to it
|
||||
* - implement any UpdateDataServiceImpl features it requires in order to forward calls to it
|
||||
*
|
||||
* ```
|
||||
* export class SomeDataService extends BaseDataService<Something> implements CreateData<Something>, SearchData<Something> {
|
||||
@@ -385,7 +385,7 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
|
||||
|
||||
/**
|
||||
* Return the links to traverse from the root of the api to the
|
||||
* endpoint this DataService represents
|
||||
* endpoint this UpdateDataServiceImpl represents
|
||||
*
|
||||
* e.g. if the api root links to 'foo', and the endpoint at 'foo'
|
||||
* links to 'bar' the linkPath for the BarDataService would be
|
||||
|
@@ -37,7 +37,7 @@ export interface CreateData<T extends CacheableObject> {
|
||||
}
|
||||
|
||||
/**
|
||||
* A DataService feature to create objects.
|
||||
* A UpdateDataServiceImpl feature to create objects.
|
||||
*
|
||||
* Concrete data services can use this feature by implementing {@link CreateData}
|
||||
* and delegating its method to an inner instance of this class.
|
||||
|
@@ -42,7 +42,7 @@ export interface FindAllData<T extends CacheableObject> {
|
||||
}
|
||||
|
||||
/**
|
||||
* A DataService feature to list all objects.
|
||||
* A UpdateDataServiceImpl feature to list all objects.
|
||||
*
|
||||
* Concrete data services can use this feature by implementing {@link FindAllData}
|
||||
* and delegating its method to an inner instance of this class.
|
||||
|
@@ -54,7 +54,7 @@ export interface PatchData<T extends CacheableObject> {
|
||||
}
|
||||
|
||||
/**
|
||||
* A DataService feature to patch and update objects.
|
||||
* A UpdateDataServiceImpl feature to patch and update objects.
|
||||
*
|
||||
* Concrete data services can use this feature by implementing {@link PatchData}
|
||||
* and delegating its method to an inner instance of this class.
|
||||
|
@@ -31,7 +31,7 @@ export interface PutData<T extends CacheableObject> {
|
||||
}
|
||||
|
||||
/**
|
||||
* A DataService feature to send PUT requests.
|
||||
* A UpdateDataServiceImpl feature to send PUT requests.
|
||||
*
|
||||
* Concrete data services can use this feature by implementing {@link PutData}
|
||||
* and delegating its method to an inner instance of this class.
|
||||
|
@@ -51,7 +51,7 @@ export interface SearchData<T extends CacheableObject> {
|
||||
}
|
||||
|
||||
/**
|
||||
* A DataService feature to search for objects.
|
||||
* A UpdateDataServiceImpl feature to search for objects.
|
||||
*
|
||||
* Concrete data services can use this feature by implementing {@link SearchData}
|
||||
* and delegating its method to an inner instance of this class.
|
||||
|
@@ -17,8 +17,8 @@ import { HALDataService } from './base/hal-data-service.interface';
|
||||
import { dataService } from './base/data-service.decorator';
|
||||
|
||||
/**
|
||||
* A DataService with only findByHref methods. Its purpose is to be used for resources that don't
|
||||
* need to be retrieved by ID, or have any way to update them, but require a DataService in order
|
||||
* A UpdateDataServiceImpl with only findByHref methods. Its purpose is to be used for resources that don't
|
||||
* need to be retrieved by ID, or have any way to update them, but require a UpdateDataServiceImpl in order
|
||||
* for their links to be resolved by the LinkService.
|
||||
*
|
||||
* an @dataService annotation can be added for any number of these resource types
|
||||
|
@@ -42,45 +42,60 @@ import {
|
||||
} from './request.models';
|
||||
import { RequestService } from './request.service';
|
||||
import { RestRequestMethod } from './rest-request-method';
|
||||
import { UpdateDataService } from './update-data.service';
|
||||
import { GenericConstructor } from '../shared/generic-constructor';
|
||||
import { NoContent } from '../shared/NoContent.model';
|
||||
import { CacheableObject } from '../cache/cacheable-object.model';
|
||||
import { CoreState } from '../core-state.model';
|
||||
import { FindListOptions } from './find-list-options.model';
|
||||
import { BaseDataService } from "./base/base-data.service";
|
||||
import { FindAllData, FindAllDataImpl } from "./base/find-all-data";
|
||||
import { SearchData, SearchDataImpl } from "./base/search-data";
|
||||
import { CreateData, CreateDataImpl } from "./base/create-data";
|
||||
|
||||
export abstract class DataService<T extends CacheableObject> implements UpdateDataService<T> {
|
||||
protected abstract requestService: RequestService;
|
||||
protected abstract rdbService: RemoteDataBuildService;
|
||||
export interface UpdateDataService<T> {
|
||||
patch(dso: T, operations: Operation[]): Observable<RemoteData<T>>;
|
||||
update(object: T): Observable<RemoteData<T>>;
|
||||
commitUpdates(method?: RestRequestMethod);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* Custom methods are:
|
||||
*
|
||||
* patch - Sends a patch request to modify current object
|
||||
* update - Add a new patch to the object cache, diff between given object and cache
|
||||
* commitUpdates - Sends the updates to the server
|
||||
*/
|
||||
|
||||
|
||||
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 linkPath: string;
|
||||
protected abstract halService: HALEndpointService;
|
||||
protected abstract objectCache: ObjectCacheService;
|
||||
protected abstract notificationsService: NotificationsService;
|
||||
protected abstract http: HttpClient;
|
||||
protected abstract comparator: ChangeAnalyzer<T>;
|
||||
|
||||
/**
|
||||
* Allows subclasses to reset the response cache time.
|
||||
*/
|
||||
protected responseMsToLive: number;
|
||||
private findAllData: FindAllDataImpl<T>;
|
||||
private searchData: SearchDataImpl<T>;
|
||||
private createData: CreateData<T>;
|
||||
|
||||
/**
|
||||
* Get the endpoint for browsing
|
||||
* @param options The [[FindListOptions]] object
|
||||
* @param linkPath The link path for the object
|
||||
* @returns {Observable<string>}
|
||||
*/
|
||||
getBrowseEndpoint(options: FindListOptions = {}, linkPath?: string): Observable<string> {
|
||||
return this.getEndpoint();
|
||||
|
||||
constructor(
|
||||
protected linkPath: string,
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected responseMsToLive: number,
|
||||
) {
|
||||
super(linkPath, requestService, rdbService, objectCache, halService, 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.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService ,this.responseMsToLive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base endpoint for all requests
|
||||
*/
|
||||
protected getEndpoint(): Observable<string> {
|
||||
return this.halService.getEndpoint(this.linkPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the HREF with given options object
|
||||
@@ -92,16 +107,7 @@ export abstract class DataService<T extends CacheableObject> implements UpdateDa
|
||||
* @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> {
|
||||
let endpoint$: Observable<string>;
|
||||
const args = [];
|
||||
|
||||
endpoint$ = this.getBrowseEndpoint(options).pipe(
|
||||
filter((href: string) => isNotEmpty(href)),
|
||||
map((href: string) => isNotEmpty(linkPath) ? `${href}/${linkPath}` : href),
|
||||
distinctUntilChanged()
|
||||
);
|
||||
|
||||
return endpoint$.pipe(map((result: string) => this.buildHrefFromFindOptions(result, options, args, ...linksToFollow)));
|
||||
return this.findAllData.getFindAllHref(options, linkPath, ...linksToFollow)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,149 +120,7 @@ export abstract class DataService<T extends CacheableObject> implements UpdateDa
|
||||
* @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> {
|
||||
let result$: Observable<string>;
|
||||
const args = [];
|
||||
|
||||
result$ = this.getSearchEndpoint(searchMethod);
|
||||
|
||||
return result$.pipe(map((result: string) => this.buildHrefFromFindOptions(result, options, args, ...linksToFollow)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn an options object into a query string and combine it with the given HREF
|
||||
*
|
||||
* @param href The HREF to which the query string should be appended
|
||||
* @param options The [[FindListOptions]] object
|
||||
* @param extraArgs Array with additional params to combine with query string
|
||||
* @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 buildHrefFromFindOptions(href: string, options: FindListOptions, extraArgs: string[] = [], ...linksToFollow: FollowLinkConfig<T>[]): string {
|
||||
let args = [...extraArgs];
|
||||
|
||||
if (hasValue(options.currentPage) && typeof options.currentPage === 'number') {
|
||||
/* TODO: this is a temporary fix for the pagination start index (0 or 1) discrepancy between the rest and the frontend respectively */
|
||||
args = this.addHrefArg(href, args, `page=${options.currentPage - 1}`);
|
||||
}
|
||||
if (hasValue(options.elementsPerPage)) {
|
||||
args = this.addHrefArg(href, args, `size=${options.elementsPerPage}`);
|
||||
}
|
||||
if (hasValue(options.sort)) {
|
||||
args = this.addHrefArg(href, args, `sort=${options.sort.field},${options.sort.direction}`);
|
||||
}
|
||||
if (hasValue(options.startsWith)) {
|
||||
args = this.addHrefArg(href, args, `startsWith=${options.startsWith}`);
|
||||
}
|
||||
if (hasValue(options.searchParams)) {
|
||||
options.searchParams.forEach((param: RequestParam) => {
|
||||
args = this.addHrefArg(href, args, `${param.fieldName}=${param.fieldValue}`);
|
||||
});
|
||||
}
|
||||
args = this.addEmbedParams(href, args, ...linksToFollow);
|
||||
if (isNotEmpty(args)) {
|
||||
return new URLCombiner(href, `?${args.join('&')}`).toString();
|
||||
} else {
|
||||
return href;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn an array of RequestParam into a query string and combine it with the given HREF
|
||||
*
|
||||
* @param href The HREF to which the query string should be appended
|
||||
* @param params Array with additional params to combine with query string
|
||||
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
|
||||
*
|
||||
* @return {Observable<string>}
|
||||
* Return an observable that emits created HREF
|
||||
*/
|
||||
buildHrefWithParams(href: string, params: RequestParam[], ...linksToFollow: FollowLinkConfig<T>[]): string {
|
||||
|
||||
let args = [];
|
||||
if (hasValue(params)) {
|
||||
params.forEach((param: RequestParam) => {
|
||||
args = this.addHrefArg(href, args, `${param.fieldName}=${param.fieldValue}`);
|
||||
});
|
||||
}
|
||||
|
||||
args = this.addEmbedParams(href, args, ...linksToFollow);
|
||||
|
||||
if (isNotEmpty(args)) {
|
||||
return new URLCombiner(href, `?${args.join('&')}`).toString();
|
||||
} else {
|
||||
return href;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Adds the embed options to the link for the request
|
||||
* @param href The href the params are to be added to
|
||||
* @param args params for the query string
|
||||
* @param linksToFollow links we want to embed in query string if shouldEmbed is true
|
||||
*/
|
||||
protected addEmbedParams(href: string, args: string[], ...linksToFollow: FollowLinkConfig<T>[]) {
|
||||
linksToFollow.forEach((linkToFollow: FollowLinkConfig<T>) => {
|
||||
if (hasValue(linkToFollow) && linkToFollow.shouldEmbed) {
|
||||
const embedString = 'embed=' + String(linkToFollow.name);
|
||||
// Add the embeds size if given in the FollowLinkConfig.FindListOptions
|
||||
if (hasValue(linkToFollow.findListOptions) && hasValue(linkToFollow.findListOptions.elementsPerPage)) {
|
||||
args = this.addHrefArg(href, args,
|
||||
'embed.size=' + String(linkToFollow.name) + '=' + linkToFollow.findListOptions.elementsPerPage);
|
||||
}
|
||||
// Adds the nested embeds and their size if given
|
||||
if (isNotEmpty(linkToFollow.linksToFollow)) {
|
||||
args = this.addNestedEmbeds(embedString, href, args, ...linkToFollow.linksToFollow);
|
||||
} else {
|
||||
args = this.addHrefArg(href, args, embedString);
|
||||
}
|
||||
}
|
||||
});
|
||||
return args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new argument to the list of arguments, only if it doesn't already exist in the given href,
|
||||
* or the current list of arguments
|
||||
*
|
||||
* @param href The href the arguments are to be added to
|
||||
* @param currentArgs The current list of arguments
|
||||
* @param newArg The new argument to add
|
||||
* @return The next list of arguments, with newArg included if it wasn't already.
|
||||
* Note this function will not modify any of the input params.
|
||||
*/
|
||||
protected addHrefArg(href: string, currentArgs: string[], newArg: string): string[] {
|
||||
if (href.includes(newArg) || currentArgs.includes(newArg)) {
|
||||
return [...currentArgs];
|
||||
} else {
|
||||
return [...currentArgs, newArg];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the nested followLinks to the embed param, separated by a /, and their sizes, recursively
|
||||
* @param embedString embedString so far (recursive)
|
||||
* @param href The href the params are to be added to
|
||||
* @param args params for the query string
|
||||
* @param linksToFollow links we want to embed in query string if shouldEmbed is true
|
||||
*/
|
||||
protected addNestedEmbeds(embedString: string, href: string, args: string[], ...linksToFollow: FollowLinkConfig<T>[]): string[] {
|
||||
let nestEmbed = embedString;
|
||||
linksToFollow.forEach((linkToFollow: FollowLinkConfig<T>) => {
|
||||
if (hasValue(linkToFollow) && linkToFollow.shouldEmbed) {
|
||||
nestEmbed = nestEmbed + '/' + String(linkToFollow.name);
|
||||
// Add the nested embeds size if given in the FollowLinkConfig.FindListOptions
|
||||
if (hasValue(linkToFollow.findListOptions) && hasValue(linkToFollow.findListOptions.elementsPerPage)) {
|
||||
const nestedEmbedSize = 'embed.size=' + nestEmbed.split('=')[1] + '=' + linkToFollow.findListOptions.elementsPerPage;
|
||||
args = this.addHrefArg(href, args, nestedEmbedSize);
|
||||
}
|
||||
if (hasValue(linkToFollow.linksToFollow) && isNotEmpty(linkToFollow.linksToFollow)) {
|
||||
args = this.addNestedEmbeds(nestEmbed, href, args, ...linkToFollow.linksToFollow);
|
||||
} else {
|
||||
args = this.addHrefArg(href, args, nestEmbed);
|
||||
}
|
||||
}
|
||||
});
|
||||
return args;
|
||||
return this.searchData.getSearchByHref(searchMethod, options, ...linksToFollow)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,7 +138,7 @@ export abstract class DataService<T extends CacheableObject> implements UpdateDa
|
||||
* Return an observable that emits object list
|
||||
*/
|
||||
findAll(options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<T>[]): Observable<RemoteData<PaginatedList<T>>> {
|
||||
return this.findAllByHref(this.getFindAllHref(options), options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,118 +201,6 @@ export abstract class DataService<T extends CacheableObject> implements UpdateDa
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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. Can be a string or
|
||||
* an Observable<string>
|
||||
* @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<T>[]): Observable<RemoteData<T>> {
|
||||
if (typeof href$ === 'string') {
|
||||
href$ = observableOf(href$);
|
||||
}
|
||||
|
||||
const requestHref$ = href$.pipe(
|
||||
isNotEmptyOperator(),
|
||||
take(1),
|
||||
map((href: string) => this.buildHrefFromFindOptions(href, {}, [], ...linksToFollow))
|
||||
);
|
||||
|
||||
this.createAndSendGetRequest(requestHref$, useCachedVersionIfAvailable);
|
||||
|
||||
return this.rdbService.buildSingle<T>(requestHref$, ...linksToFollow).pipe(
|
||||
// This skip ensures that if a stale object is present in the cache when you do a
|
||||
// call it isn't immediately returned, but we wait until the remote data for the new request
|
||||
// is created. If useCachedVersionIfAvailable is false it also ensures you don't get a
|
||||
// cached completed object
|
||||
skipWhile((rd: RemoteData<T>) => useCachedVersionIfAvailable ? rd.isStale : rd.hasCompleted),
|
||||
this.reRequestStaleRemoteData(reRequestOnStale, () =>
|
||||
this.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. Can be a string or
|
||||
* an Observable<string>
|
||||
* @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<T>[]): Observable<RemoteData<PaginatedList<T>>> {
|
||||
if (typeof href$ === 'string') {
|
||||
href$ = observableOf(href$);
|
||||
}
|
||||
|
||||
const requestHref$ = href$.pipe(
|
||||
isNotEmptyOperator(),
|
||||
take(1),
|
||||
map((href: string) => this.buildHrefFromFindOptions(href, findListOptions, [], ...linksToFollow))
|
||||
);
|
||||
|
||||
this.createAndSendGetRequest(requestHref$, useCachedVersionIfAvailable);
|
||||
|
||||
return this.rdbService.buildList<T>(requestHref$, ...linksToFollow).pipe(
|
||||
// This skip ensures that if a stale object is present in the cache when you do a
|
||||
// call it isn't immediately returned, but we wait until the remote data for the new request
|
||||
// is created. If useCachedVersionIfAvailable is false it also ensures you don't get a
|
||||
// cached completed object
|
||||
skipWhile((rd: RemoteData<PaginatedList<T>>) => useCachedVersionIfAvailable ? rd.isStale : rd.hasCompleted),
|
||||
this.reRequestStaleRemoteData(reRequestOnStale, () =>
|
||||
this.findAllByHref(href$, findListOptions, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a GET request for the given href, and send it.
|
||||
*
|
||||
* @param href$ The url of object we want to retrieve. Can be a string or
|
||||
* an Observable<string>
|
||||
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
|
||||
* no valid cached version. Defaults to true
|
||||
*/
|
||||
protected createAndSendGetRequest(href$: string | Observable<string>, useCachedVersionIfAvailable = true): void {
|
||||
if (isNotEmpty(href$)) {
|
||||
if (typeof href$ === 'string') {
|
||||
href$ = observableOf(href$);
|
||||
}
|
||||
|
||||
href$.pipe(
|
||||
isNotEmptyOperator(),
|
||||
take(1)
|
||||
).subscribe((href: string) => {
|
||||
const requestId = this.requestService.generateRequestId();
|
||||
const request = new GetRequest(requestId, href);
|
||||
if (hasValue(this.responseMsToLive)) {
|
||||
request.responseMsToLive = this.responseMsToLive;
|
||||
}
|
||||
this.requestService.send(request, useCachedVersionIfAvailable);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return object search endpoint by given search method
|
||||
*
|
||||
* @param searchMethod The search method for the object
|
||||
*/
|
||||
protected getSearchEndpoint(searchMethod: string): Observable<string> {
|
||||
return this.halService.getEndpoint(this.linkPath).pipe(
|
||||
filter((href: string) => isNotEmpty(href)),
|
||||
map((href: string) => `${href}/search/${searchMethod}`));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a new FindListRequest with given search method
|
||||
*
|
||||
@@ -464,9 +216,7 @@ export abstract class DataService<T extends CacheableObject> implements UpdateDa
|
||||
* 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>>> {
|
||||
const hrefObs = this.getSearchByHref(searchMethod, options, ...linksToFollow);
|
||||
|
||||
return this.findAllByHref(hrefObs, undefined, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -548,38 +298,7 @@ export abstract class DataService<T extends CacheableObject> implements UpdateDa
|
||||
* Array with additional params to combine with query string
|
||||
*/
|
||||
create(object: T, ...params: RequestParam[]): Observable<RemoteData<T>> {
|
||||
const requestId = this.requestService.generateRequestId();
|
||||
const endpoint$ = this.getEndpoint().pipe(
|
||||
isNotEmptyOperator(),
|
||||
distinctUntilChanged(),
|
||||
map((endpoint: string) => this.buildHrefWithParams(endpoint, params))
|
||||
);
|
||||
|
||||
const serializedObject = new DSpaceSerializer(getClassForType(object.type)).serialize(object);
|
||||
|
||||
endpoint$.pipe(
|
||||
take(1)
|
||||
).subscribe((endpoint: string) => {
|
||||
const request = new CreateRequest(requestId, endpoint, JSON.stringify(serializedObject));
|
||||
if (hasValue(this.responseMsToLive)) {
|
||||
request.responseMsToLive = this.responseMsToLive;
|
||||
}
|
||||
this.requestService.send(request);
|
||||
});
|
||||
|
||||
const result$ = this.rdbService.buildFromRequestUUID<T>(requestId);
|
||||
|
||||
// TODO a dataservice is not the best place to show a notification,
|
||||
// this should move up to the components that use this method
|
||||
result$.pipe(
|
||||
takeWhile((rd: RemoteData<T>) => rd.isLoading, true)
|
||||
).subscribe((rd: RemoteData<T>) => {
|
||||
if (rd.hasFailed) {
|
||||
this.notificationsService.error('Server Error:', rd.errorMessage, new NotificationOptions(-1));
|
||||
}
|
||||
});
|
||||
|
||||
return result$;
|
||||
return this.createData.create(object, ...params)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -732,16 +451,4 @@ export abstract class DataService<T extends CacheableObject> implements UpdateDa
|
||||
commitUpdates(method?: RestRequestMethod) {
|
||||
this.requestService.commit(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the links to traverse from the root of the api to the
|
||||
* endpoint this DataService represents
|
||||
*
|
||||
* e.g. if the api root links to 'foo', and the endpoint at 'foo'
|
||||
* links to 'bar' the linkPath for the BarDataService would be
|
||||
* 'foo/bar'
|
||||
*/
|
||||
getLinkPath(): string {
|
||||
return this.linkPath;
|
||||
}
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { RemoteData } from './remote-data';
|
||||
import { RestRequestMethod } from './rest-request-method';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
|
||||
/**
|
||||
* Represents a data service to update a given object
|
||||
*/
|
||||
export interface UpdateDataService<T> {
|
||||
patch(dso: T, operations: Operation[]): Observable<RemoteData<T>>;
|
||||
update(object: T): Observable<RemoteData<T>>;
|
||||
commitUpdates(method?: RestRequestMethod);
|
||||
}
|
@@ -128,7 +128,7 @@ describe('VersionDataService test', () => {
|
||||
});
|
||||
|
||||
describe('getHistoryFromVersion', () => {
|
||||
it('should proxy the call to DataService.findByHref', () => {
|
||||
it('should proxy the call to UpdateDataServiceImpl.findByHref', () => {
|
||||
scheduler.schedule(() => service.getHistoryFromVersion(mockVersion, true, true));
|
||||
scheduler.flush();
|
||||
|
||||
|
@@ -315,7 +315,7 @@ describe('EPersonDataService', () => {
|
||||
service.deleteEPerson(EPersonMock).subscribe();
|
||||
});
|
||||
|
||||
it('should call DataService.delete with the EPerson\'s UUID', () => {
|
||||
it('should call UpdateDataServiceImpl.delete with the EPerson\'s UUID', () => {
|
||||
expect(service.delete).toHaveBeenCalledWith(EPersonMock.id);
|
||||
});
|
||||
});
|
||||
|
@@ -126,7 +126,7 @@ describe('WorkflowItemDataService test', () => {
|
||||
});
|
||||
|
||||
describe('findByItem', () => {
|
||||
it('should proxy the call to DataService.findByHref', () => {
|
||||
it('should proxy the call to UpdateDataServiceImpl.findByHref', () => {
|
||||
scheduler.schedule(() => service.findByItem('1234-1234', true, true, pageInfo));
|
||||
scheduler.flush();
|
||||
|
||||
|
@@ -141,7 +141,7 @@ describe('WorkspaceitemDataService test', () => {
|
||||
});
|
||||
|
||||
describe('findByItem', () => {
|
||||
it('should proxy the call to DataService.findByHref', () => {
|
||||
it('should proxy the call to UpdateDataServiceImpl.findByHref', () => {
|
||||
scheduler.schedule(() => service.findByItem('1234-1234', true, true, pageInfo));
|
||||
scheduler.flush();
|
||||
const searchUrl = service.getIDHref('item', [new RequestParam('uuid', encodeURIComponent('1234-1234'))]);
|
||||
|
@@ -11,7 +11,7 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { DataService } from '../data/data.service';
|
||||
import { UpdateDataServiceImpl } from '../data/update-data-service';
|
||||
import { ChangeAnalyzer } from '../data/change-analyzer';
|
||||
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
@@ -31,13 +31,9 @@ import { SuggestionTargetDataService } from './target/suggestion-target-data.ser
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
|
||||
/**
|
||||
* A private DataService implementation to delegate specific methods to.
|
||||
* A private UpdateDataServiceImpl implementation to delegate specific methods to.
|
||||
*/
|
||||
export class SuggestionDataServiceImpl extends DataService<Suggestion> {
|
||||
/**
|
||||
* The REST endpoint.
|
||||
*/
|
||||
protected linkPath = 'suggestions';
|
||||
export class SuggestionDataServiceImpl extends UpdateDataServiceImpl<Suggestion> {
|
||||
|
||||
/**
|
||||
* Initialize service variables
|
||||
@@ -49,6 +45,7 @@ export class SuggestionDataServiceImpl extends DataService<Suggestion> {
|
||||
* @param {NotificationsService} notificationsService
|
||||
* @param {HttpClient} http
|
||||
* @param {ChangeAnalyzer<Suggestion>} comparator
|
||||
* @param responseMsToLive
|
||||
*/
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
@@ -58,8 +55,10 @@ export class SuggestionDataServiceImpl extends DataService<Suggestion> {
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected http: HttpClient,
|
||||
protected comparator: ChangeAnalyzer<Suggestion>) {
|
||||
super();
|
||||
protected comparator: ChangeAnalyzer<Suggestion>,
|
||||
protected responseMsToLive: number,
|
||||
) {
|
||||
super('suggestions', requestService, rdbService, objectCache, halService, notificationsService ,responseMsToLive);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,20 +72,22 @@ export class SuggestionsDataService {
|
||||
protected searchFindByTargetAndSourceMethod = 'findByTargetAndSource';
|
||||
|
||||
/**
|
||||
* A private DataService implementation to delegate specific methods to.
|
||||
* A private UpdateDataServiceImpl implementation to delegate specific methods to.
|
||||
*/
|
||||
private suggestionsDataService: SuggestionDataServiceImpl;
|
||||
|
||||
/**
|
||||
* A private DataService implementation to delegate specific methods to.
|
||||
* A private UpdateDataServiceImpl implementation to delegate specific methods to.
|
||||
*/
|
||||
private suggestionSourcesDataService: SuggestionSourceDataService;
|
||||
|
||||
/**
|
||||
* A private DataService implementation to delegate specific methods to.
|
||||
* A private UpdateDataServiceImpl implementation to delegate specific methods to.
|
||||
*/
|
||||
private suggestionTargetsDataService: SuggestionTargetDataService;
|
||||
|
||||
private responseMsToLive = 10 * 1000;
|
||||
|
||||
/**
|
||||
* Initialize service variables
|
||||
* @param {RequestService} requestService
|
||||
@@ -98,6 +99,7 @@ export class SuggestionsDataService {
|
||||
* @param {DefaultChangeAnalyzer<Suggestion>} comparatorSuggestions
|
||||
* @param {DefaultChangeAnalyzer<SuggestionSource>} comparatorSources
|
||||
* @param {DefaultChangeAnalyzer<SuggestionTarget>} comparatorTargets
|
||||
* @param responseMsToLive
|
||||
*/
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
@@ -110,7 +112,7 @@ export class SuggestionsDataService {
|
||||
protected comparatorSources: DefaultChangeAnalyzer<SuggestionSource>,
|
||||
protected comparatorTargets: DefaultChangeAnalyzer<SuggestionTarget>,
|
||||
) {
|
||||
this.suggestionsDataService = new SuggestionDataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparatorSuggestions);
|
||||
this.suggestionsDataService = new SuggestionDataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparatorSuggestions, this.responseMsToLive);
|
||||
this.suggestionSourcesDataService = new SuggestionSourceDataService(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparatorSources);
|
||||
this.suggestionTargetsDataService = new SuggestionTargetDataService(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparatorTargets);
|
||||
}
|
||||
|
@@ -12,7 +12,6 @@ import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
||||
import {
|
||||
getFirstCompletedRemoteData,
|
||||
} from '../../core/shared/operators';
|
||||
import { UpdateDataService } from '../../core/data/update-data.service';
|
||||
import { ResourceType } from '../../core/shared/resource-type';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
@@ -22,6 +21,7 @@ import { ArrayMoveChangeAnalyzer } from '../../core/data/array-move-change-analy
|
||||
import { DATA_SERVICE_FACTORY } from '../../core/data/base/data-service.decorator';
|
||||
import { GenericConstructor } from '../../core/shared/generic-constructor';
|
||||
import { HALDataService } from '../../core/data/base/hal-data-service.interface';
|
||||
import { UpdateDataService } from "../../core/data/update-data-service";
|
||||
|
||||
@Component({
|
||||
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 { Component, Input } from '@angular/core';
|
||||
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({
|
||||
selector: 'ds-themed-dso-edit-metadata',
|
||||
|
@@ -541,7 +541,7 @@ export class MenuResolver implements Resolve<boolean> {
|
||||
{
|
||||
id: 'notifications',
|
||||
active: false,
|
||||
visible: authorized && canSeeQA,
|
||||
visible: authorized && true,
|
||||
model: {
|
||||
type: MenuItemType.TEXT,
|
||||
text: 'menu.section.notifications'
|
||||
|
@@ -26,7 +26,7 @@ import { QualityAssuranceSourceService } from './qa/source/quality-assurance-sou
|
||||
import {
|
||||
QualityAssuranceSourceDataService
|
||||
} from '../core/notifications/qa/source/quality-assurance-source-data.service';
|
||||
import { SuggestionTargetsComponent } from '../suggestion-notifications/suggestion-targets/suggestion-targets.component';
|
||||
import { PublicationClaimComponent } from '../suggestion-notifications/suggestion-targets/publication-claim/publication-claim.component';
|
||||
import { SuggestionActionsComponent } from '../suggestion-notifications/suggestion-actions/suggestion-actions.component';
|
||||
import {
|
||||
SuggestionListElementComponent
|
||||
@@ -65,7 +65,7 @@ const COMPONENTS = [
|
||||
QualityAssuranceTopicsComponent,
|
||||
QualityAssuranceEventsComponent,
|
||||
QualityAssuranceSourceComponent,
|
||||
SuggestionTargetsComponent,
|
||||
PublicationClaimComponent,
|
||||
SuggestionActionsComponent,
|
||||
SuggestionListElementComponent,
|
||||
SuggestionEvidencesComponent,
|
||||
|
@@ -96,7 +96,7 @@ export class EpersonGroupListComponent implements OnInit, OnDestroy {
|
||||
private pageConfigSub: Subscription;
|
||||
|
||||
/**
|
||||
* Initialize instance variables and inject the properly DataService
|
||||
* Initialize instance variables and inject the properly UpdateDataServiceImpl
|
||||
*
|
||||
* @param {DSONameService} dsoNameService
|
||||
* @param {Injector} parentInjector
|
||||
|
@@ -13,7 +13,7 @@ import { CacheableObject } from '../../core/cache/cacheable-object.model';
|
||||
import { IdentifiableDataService } from '../../core/data/base/identifiable-data.service';
|
||||
|
||||
/**
|
||||
* Class to return DataService for given ResourceType
|
||||
* Class to return UpdateDataServiceImpl for given ResourceType
|
||||
*/
|
||||
export class MyDSpaceActionsServiceFactory<T extends CacheableObject, TService extends IdentifiableDataService<T>> {
|
||||
public getConstructor(type: ResourceType): TService {
|
||||
|
@@ -47,7 +47,7 @@ export abstract class MyDSpaceActionsComponent<T extends DSpaceObject, TService
|
||||
public processing$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
/**
|
||||
* Instance of DataService related to mydspace object
|
||||
* Instance of UpdateDataServiceImpl related to mydspace object
|
||||
*/
|
||||
protected objectDataService: TService;
|
||||
|
||||
|
@@ -23,7 +23,7 @@
|
||||
{{ ignoreSuggestionLabel() | translate}}</button>
|
||||
<button *ngIf="!isBulk" (click)="toggleSeeEvidences()" [disabled]="!hasEvidence" class="btn btn-info ml-2">
|
||||
<i class="fa fa-eye"></i>
|
||||
<ng-container *ngIf="!seeEvidence"> {{ 'reciter.suggestion.seeEvidence' | translate}}</ng-container>
|
||||
<ng-container *ngIf="seeEvidence"> {{ 'reciter.suggestion.hideEvidence' | translate}}</ng-container>
|
||||
<ng-container *ngIf="!seeEvidence"> {{ 'suggestion.seeEvidence' | translate}}</ng-container>
|
||||
<ng-container *ngIf="seeEvidence"> {{ 'suggestion.hideEvidence' | translate}}</ng-container>
|
||||
</button>
|
||||
</div>
|
||||
|
@@ -1,9 +1,9 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h1 id="header" class="border-bottom pb-2">{{'reciter.suggestion.title'| translate}}</h1>
|
||||
<h1 id="header" class="border-bottom pb-2">{{'suggestion.title'| translate}}</h1>
|
||||
|
||||
<ds-loading class="container" *ngIf="(isTargetsLoading() | async)" message="{{'reciter.suggestion.loading' | translate}}"></ds-loading>
|
||||
<ds-loading class="container" *ngIf="(isTargetsLoading() | async)" message="{{'suggestion.loading' | translate}}"></ds-loading>
|
||||
<ds-pagination *ngIf="!(isTargetsLoading() | async)"
|
||||
[paginationOptions]="paginationConfig"
|
||||
[collectionSize]="(totalElements$ | async)"
|
||||
@@ -11,17 +11,17 @@
|
||||
[retainScrollPosition]="false"
|
||||
(paginationChange)="getSuggestionTargets()">
|
||||
|
||||
<ds-loading class="container" *ngIf="(isTargetsProcessing() | async)" message="'reciter.suggestion.loading' | translate"></ds-loading>
|
||||
<ds-loading class="container" *ngIf="(isTargetsProcessing() | async)" message="'suggestion.loading' | translate"></ds-loading>
|
||||
<ng-container *ngIf="!(isTargetsProcessing() | async)">
|
||||
<div *ngIf="!(targets$|async) || (targets$|async)?.length == 0" class="alert alert-info w-100 mb-2 mt-2" role="alert">
|
||||
{{'reciter.suggestion.noTargets' | translate}}
|
||||
{{'suggestion.noTargets' | translate}}
|
||||
</div>
|
||||
<div *ngIf="(targets$|async) && (targets$|async)?.length != 0" class="table-responsive mt-2">
|
||||
<table id="epeople" class="table table-striped table-hover table-bordered">
|
||||
<thead>
|
||||
<tr class="text-center">
|
||||
<th scope="col">{{'reciter.suggestion.table.name' | translate}}</th>
|
||||
<th scope="col">{{'reciter.suggestion.table.actions' | translate}}</th>
|
||||
<th scope="col">{{'suggestion.table.name' | translate}}</th>
|
||||
<th scope="col">{{'suggestion.table.actions' | translate}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -33,9 +33,9 @@
|
||||
<div class="btn-group edit-field">
|
||||
<button (click)="redirectToSuggestions(targetElement.id, targetElement.display)"
|
||||
class="btn btn-outline-primary btn-sm"
|
||||
title="{{('reciter.suggestion.button.review.title' | translate: { total: targetElement.total.toString() }) +
|
||||
title="{{('suggestion.button.review.title' | translate: { total: targetElement.total.toString() }) +
|
||||
targetElement.display}}">
|
||||
<span>{{'reciter.suggestion.button.review' | translate: { total: targetElement.total.toString() } }} </span>
|
||||
<span>{{'suggestion.button.review' | translate: { total: targetElement.total.toString() } }} </span>
|
||||
<i class="fas fa-lightbulb"></i>
|
||||
</button>
|
||||
</div>
|
@@ -4,23 +4,23 @@ import { Router } from '@angular/router';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { distinctUntilChanged, take } from 'rxjs/operators';
|
||||
|
||||
import { SuggestionTarget } from '../../core/suggestion-notifications/models/suggestion-target.model';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||
import { SuggestionTargetsStateService } from './suggestion-targets.state.service';
|
||||
import { getSuggestionPageRoute } from '../../suggestions-page/suggestions-page-routing-paths';
|
||||
import { SuggestionsService } from '../suggestions.service';
|
||||
import { PaginationService } from '../../core/pagination/pagination.service';
|
||||
import { SuggestionTarget } from '../../../core/suggestion-notifications/models/suggestion-target.model';
|
||||
import { hasValue } from '../../../shared/empty.util';
|
||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||
import { SuggestionTargetsStateService } from '../suggestion-targets.state.service';
|
||||
import { getSuggestionPageRoute } from '../../../suggestions-page/suggestions-page-routing-paths';
|
||||
import { SuggestionsService } from '../../suggestions.service';
|
||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||
|
||||
/**
|
||||
* Component to display the Suggestion Target list.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-suggestion-target',
|
||||
templateUrl: './suggestion-targets.component.html',
|
||||
styleUrls: ['./suggestion-targets.component.scss'],
|
||||
selector: 'ds-publication-claim',
|
||||
templateUrl: './publication-claim.component.html',
|
||||
styleUrls: ['./publication-claim.component.scss'],
|
||||
})
|
||||
export class SuggestionTargetsComponent implements OnInit {
|
||||
export class PublicationClaimComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* The source for which to list targets
|
@@ -156,7 +156,7 @@ describe('SuggestionsService test', () => {
|
||||
});
|
||||
|
||||
it('should delete suggestions', () => {
|
||||
spyOn(service, 'notMine');
|
||||
spyOn(service, 'ignoreSuggestion');
|
||||
service.ignoreSuggestionMultiple([mockSuggestionPublicationOne]);
|
||||
expect(service.ignoreSuggestion).toHaveBeenCalledWith(mockSuggestionPublicationOne.id);
|
||||
});
|
||||
|
@@ -18,7 +18,7 @@ import { combineLatest, Subject } from 'rxjs';
|
||||
})
|
||||
export class SuggestionsPopupComponent implements OnInit, OnDestroy {
|
||||
|
||||
labelPrefix = 'mydspace.';
|
||||
labelPrefix = 'notification.';
|
||||
|
||||
subscription;
|
||||
|
||||
@@ -57,7 +57,7 @@ export class SuggestionsPopupComponent implements OnInit, OnDestroy {
|
||||
* @private
|
||||
*/
|
||||
private showNotificationForNewSuggestions(suggestionTarget: SuggestionTarget): void {
|
||||
const content = this.translateService.instant(this.labelPrefix + 'notification.suggestion',
|
||||
const content = this.translateService.instant(this.labelPrefix + 'suggestion',
|
||||
this.suggestionsService.getNotificationSuggestionInterpolation(suggestionTarget));
|
||||
this.notificationsService.success('', content, {timeOut:0}, true);
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ import { NoContent } from '../core/shared/NoContent.model';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { WorkspaceItem } from '../core/submission/models/workspaceitem.model';
|
||||
import {FindListOptions} from '../core/data/find-list-options.model';
|
||||
import {SuggestionConfig} from '../../config/layout-config.interfaces';
|
||||
import {SuggestionConfig} from '../../config/suggestion-config.interfaces';
|
||||
import { ResearcherProfileDataService } from '../core/profile/researcher-profile-data.service';
|
||||
import {
|
||||
SuggestionSourceDataService
|
||||
@@ -229,7 +229,7 @@ export class SuggestionsService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a bulk notMine operation.
|
||||
* Perform a bulk ignoreSuggestion operation.
|
||||
* @param suggestions the array containing the suggestions
|
||||
*/
|
||||
public ignoreSuggestionMultiple(suggestions: Suggestion[]): Observable<SuggestionBulkResult> {
|
||||
|
@@ -41,7 +41,6 @@ describe('SuggestionPageComponent', () => {
|
||||
let scheduler: TestScheduler;
|
||||
const mockSuggestionsService = getMockSuggestionsService();
|
||||
const mockSuggestionsTargetStateService = getMockSuggestionNotificationsStateService();
|
||||
const suggestionTargetsList: PaginatedList<Suggestion> = buildPaginatedList(new PageInfo(), [mockSuggestionPublicationOne, mockSuggestionPublicationTwo]);
|
||||
const router = new RouterStub();
|
||||
const routeStub = {
|
||||
data: observableOf({
|
||||
@@ -139,7 +138,7 @@ describe('SuggestionPageComponent', () => {
|
||||
scheduler.schedule(() => fixture.detectChanges());
|
||||
scheduler.flush();
|
||||
component.ignoreSuggestion('1');
|
||||
expect(mockSuggestionsService.notMine).toHaveBeenCalledWith('1');
|
||||
expect(mockSuggestionsService.ignoreSuggestion).toHaveBeenCalledWith('1');
|
||||
expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled();
|
||||
expect(component.updatePage).toHaveBeenCalled();
|
||||
});
|
||||
@@ -150,7 +149,7 @@ describe('SuggestionPageComponent', () => {
|
||||
scheduler.schedule(() => fixture.detectChanges());
|
||||
scheduler.flush();
|
||||
component.ignoreSuggestionAllSelected();
|
||||
expect(mockSuggestionsService.notMineMultiple).toHaveBeenCalled();
|
||||
expect(mockSuggestionsService.ignoreSuggestionMultiple).toHaveBeenCalled();
|
||||
expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled();
|
||||
expect(component.updatePage).toHaveBeenCalled();
|
||||
});
|
||||
|
@@ -28,6 +28,11 @@ import {redirectOn4xx} from '../core/shared/authorized.operators';
|
||||
templateUrl: './suggestions-page.component.html',
|
||||
styleUrls: ['./suggestions-page.component.scss'],
|
||||
})
|
||||
|
||||
/**
|
||||
* Component used to visualize one of the suggestions from the publication claim page or from the notification pop up
|
||||
*/
|
||||
|
||||
export class SuggestionsPageComponent implements OnInit {
|
||||
|
||||
/**
|
||||
@@ -139,15 +144,6 @@ export class SuggestionsPageComponent implements OnInit {
|
||||
this.processing$.next(false);
|
||||
this.suggestionsRD$.next(results);
|
||||
this.suggestionService.clearSuggestionRequests();
|
||||
// navigate to the mydspace if no suggestions remains
|
||||
|
||||
// if (results.totalElements === 0) {
|
||||
// const content = this.translateService.instant('reciter.suggestion.empty',
|
||||
// this.suggestionService.getNotificationSuggestionInterpolation(this.suggestionTarget));
|
||||
// this.notificationService.success('', content, {timeOut:0}, true);
|
||||
// TODO if the target is not the current use route to the suggestion target page
|
||||
// this.router.navigate(['/mydspace']);
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -3084,9 +3084,9 @@
|
||||
|
||||
"mydspace.import": "Import",
|
||||
|
||||
"mydspace.notification.suggestion": "We found <b>{{count}} publications</b><br> in the {{source}} that seems to be related to your profile.<br> Please <a href='/suggestions/{{suggestionId}}'>review the suggestions</a>",
|
||||
"notification.suggestion": "We found <b>{{count}} publications</b><br> in the {{source}} that seems to be related to your profile.<br> Please <a href='/suggestions/{{suggestionId}}'>review the suggestions</a>",
|
||||
|
||||
"mydspace.notification.suggestion.page": "We found <b>{{count}} {{type}}</b> in the {{source}} that seems to be related to your profile. Please <a href='/suggestions/{{suggestionId}}'>review the suggestions.</a>",
|
||||
"notification.suggestion.page": "We found <b>{{count}} {{type}}</b> in the {{source}} that seems to be related to your profile. Please <a href='/suggestions/{{suggestionId}}'>review the suggestions.</a>",
|
||||
|
||||
"nav.browse.header": "All of DSpace",
|
||||
|
||||
|
@@ -14,7 +14,7 @@ import { AuthConfig } from './auth-config.interfaces';
|
||||
import { UIServerConfig } from './ui-server-config.interface';
|
||||
import { MediaViewerConfig } from './media-viewer-config.interface';
|
||||
import { BrowseByConfig } from './browse-by-config.interface';
|
||||
import { SuggestionConfig } from './layout-config.interfaces';
|
||||
import { SuggestionConfig } from './suggestion-config.interfaces';
|
||||
import { BundleConfig } from './bundle-config.interface';
|
||||
import { ActuatorsConfig } from './actuators.config';
|
||||
import { InfoConfig } from './info-config.interface';
|
||||
|
@@ -14,7 +14,7 @@ import { ServerConfig } from './server-config.interface';
|
||||
import { SubmissionConfig } from './submission-config.interface';
|
||||
import { ThemeConfig } from './theme.config';
|
||||
import { UIServerConfig } from './ui-server-config.interface';
|
||||
import {SuggestionConfig} from './layout-config.interfaces';
|
||||
import {SuggestionConfig} from './suggestion-config.interfaces';
|
||||
import { BundleConfig } from './bundle-config.interface';
|
||||
import { ActuatorsConfig } from './actuators.config';
|
||||
import { InfoConfig } from './info-config.interface';
|
||||
@@ -304,6 +304,8 @@ export class DefaultAppConfig implements AppConfig {
|
||||
// source: 'suggestionSource',
|
||||
// collectionId: 'collectionUUID'
|
||||
// }
|
||||
// If not mapped the suggestion service won't be able to approve and import the related suggestion
|
||||
// or load the fixed suggestions collections that can be configured here adding the source and the UUID of the collection
|
||||
];
|
||||
|
||||
// Theme Config
|
||||
|
@@ -1,46 +0,0 @@
|
||||
import { Config } from './config.interface';
|
||||
|
||||
export interface UrnConfig extends Config {
|
||||
name: string;
|
||||
baseUrl: string;
|
||||
}
|
||||
|
||||
export interface CrisRefConfig extends Config {
|
||||
entityType: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
export interface CrisLayoutMetadataBoxConfig extends Config {
|
||||
defaultMetadataLabelColStyle: string;
|
||||
defaultMetadataValueColStyle: string;
|
||||
}
|
||||
|
||||
export interface CrisLayoutTypeConfig {
|
||||
orientation: string;
|
||||
}
|
||||
|
||||
export interface NavbarConfig extends Config {
|
||||
showCommunityCollection: boolean;
|
||||
}
|
||||
|
||||
export interface CrisItemPageConfig extends Config {
|
||||
[entity: string]: CrisLayoutTypeConfig;
|
||||
default: CrisLayoutTypeConfig;
|
||||
}
|
||||
|
||||
|
||||
export interface CrisLayoutConfig extends Config {
|
||||
urn: UrnConfig[];
|
||||
crisRef: CrisRefConfig[];
|
||||
itemPage: CrisItemPageConfig;
|
||||
metadataBox: CrisLayoutMetadataBoxConfig;
|
||||
}
|
||||
|
||||
export interface LayoutConfig extends Config {
|
||||
navbar: NavbarConfig;
|
||||
}
|
||||
|
||||
export interface SuggestionConfig extends Config {
|
||||
source: string;
|
||||
collectionId: string;
|
||||
}
|
6
src/config/suggestion-config.interfaces.ts
Normal file
6
src/config/suggestion-config.interfaces.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { Config } from "./config.interface";
|
||||
|
||||
export interface SuggestionConfig extends Config {
|
||||
source: string;
|
||||
collectionId: string;
|
||||
}
|
Reference in New Issue
Block a user