mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Merge pull request #978 from 4Science/CST-3620
Update /claimedtasks and /pooltasks to support reloading of workflow actions on individual WorkflowItems
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
[configurationList]="(configurationList$ | async)"
|
[configurationList]="(configurationList$ | async)"
|
||||||
[resultCount]="(resultsRD$ | async)?.payload.totalElements"
|
[resultCount]="(resultsRD$ | async)?.payload.totalElements"
|
||||||
[viewModeList]="viewModeList"
|
[viewModeList]="viewModeList"
|
||||||
|
[refreshFilters]="refreshFilters.asObservable()"
|
||||||
[inPlaceSearch]="inPlaceSearch"></ds-search-sidebar>
|
[inPlaceSearch]="inPlaceSearch"></ds-search-sidebar>
|
||||||
<div class="col-12 col-md-9">
|
<div class="col-12 col-md-9">
|
||||||
<ds-search-form id="search-form"
|
<ds-search-form id="search-form"
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
[resultCount]="(resultsRD$ | async)?.payload.totalElements"
|
[resultCount]="(resultsRD$ | async)?.payload.totalElements"
|
||||||
(toggleSidebar)="closeSidebar()"
|
(toggleSidebar)="closeSidebar()"
|
||||||
[ngClass]="{'active': !(isSidebarCollapsed() | async)}"
|
[ngClass]="{'active': !(isSidebarCollapsed() | async)}"
|
||||||
|
[refreshFilters]="refreshFilters.asObservable()"
|
||||||
[inPlaceSearch]="inPlaceSearch">
|
[inPlaceSearch]="inPlaceSearch">
|
||||||
</ds-search-sidebar>
|
</ds-search-sidebar>
|
||||||
<div id="search-content" class="col-12">
|
<div id="search-content" class="col-12">
|
||||||
@@ -39,7 +41,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<ds-my-dspace-results [searchResults]="resultsRD$ | async"
|
<ds-my-dspace-results [searchResults]="resultsRD$ | async"
|
||||||
[searchConfig]="searchOptions$ | async"
|
[searchConfig]="searchOptions$ | async"
|
||||||
[context]="context$ | async"></ds-my-dspace-results>
|
[context]="context$ | async"
|
||||||
|
(contentChange)="onResultsContentChange()"></ds-my-dspace-results>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -7,7 +7,7 @@ import {
|
|||||||
OnInit
|
OnInit
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
import { BehaviorSubject, Observable, Subject, Subscription } from 'rxjs';
|
||||||
import { map, switchMap, tap, } from 'rxjs/operators';
|
import { map, switchMap, tap, } from 'rxjs/operators';
|
||||||
|
|
||||||
import { PaginatedList } from '../core/data/paginated-list.model';
|
import { PaginatedList } from '../core/data/paginated-list.model';
|
||||||
@@ -101,6 +101,11 @@ export class MyDSpacePageComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
context$: Observable<Context>;
|
context$: Observable<Context>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit an event every time search sidebars must refresh their contents.
|
||||||
|
*/
|
||||||
|
refreshFilters: Subject<any> = new Subject<any>();
|
||||||
|
|
||||||
constructor(private service: SearchService,
|
constructor(private service: SearchService,
|
||||||
private sidebarService: SidebarService,
|
private sidebarService: SidebarService,
|
||||||
private windowService: HostWindowService,
|
private windowService: HostWindowService,
|
||||||
@@ -148,6 +153,14 @@ export class MyDSpacePageComponent implements OnInit {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the contentChange event from within the my dspace content.
|
||||||
|
* Notify search sidebars to refresh their content.
|
||||||
|
*/
|
||||||
|
onResultsContentChange() {
|
||||||
|
this.refreshFilters.next();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the sidebar to a collapsed state
|
* Set the sidebar to a collapsed state
|
||||||
*/
|
*/
|
||||||
@@ -184,5 +197,6 @@ export class MyDSpacePageComponent implements OnInit {
|
|||||||
if (hasValue(this.sub)) {
|
if (hasValue(this.sub)) {
|
||||||
this.sub.unsubscribe();
|
this.sub.unsubscribe();
|
||||||
}
|
}
|
||||||
|
this.refreshFilters.complete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,8 @@
|
|||||||
[sortConfig]="searchConfig.sort"
|
[sortConfig]="searchConfig.sort"
|
||||||
[objects]="searchResults"
|
[objects]="searchResults"
|
||||||
[hideGear]="true"
|
[hideGear]="true"
|
||||||
[context]="context">
|
[context]="context"
|
||||||
|
(contentChange)="contentChange.emit()">
|
||||||
</ds-viewable-collection>
|
</ds-viewable-collection>
|
||||||
</div>
|
</div>
|
||||||
<ds-loading *ngIf="isLoading()" message="{{'loading.mydspace-results' | translate}}"></ds-loading>
|
<ds-loading *ngIf="isLoading()" message="{{'loading.mydspace-results' | translate}}"></ds-loading>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||||
import { fadeIn, fadeInOut } from '../../shared/animations/fade';
|
import { fadeIn, fadeInOut } from '../../shared/animations/fade';
|
||||||
@@ -41,6 +41,12 @@ export class MyDSpaceResultsComponent {
|
|||||||
* The current context for the search results
|
* The current context for the search results
|
||||||
*/
|
*/
|
||||||
@Input() context: Context;
|
@Input() context: Context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit when one of the results has changed.
|
||||||
|
*/
|
||||||
|
@Output() contentChange = new EventEmitter<any>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A boolean representing if search results entry are separated by a line
|
* A boolean representing if search results entry are separated by a line
|
||||||
*/
|
*/
|
||||||
|
@@ -14,12 +14,16 @@ import { ClaimedTaskSearchResultDetailElementComponent } from '../shared/object-
|
|||||||
import { ItemSearchResultListElementSubmissionComponent } from '../shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component';
|
import { ItemSearchResultListElementSubmissionComponent } from '../shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component';
|
||||||
import { WorkflowItemSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component';
|
import { WorkflowItemSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component';
|
||||||
import { PoolSearchResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component';
|
import { PoolSearchResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component';
|
||||||
|
import { ClaimedApprovedSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component';
|
||||||
|
import { ClaimedDeclinedSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component';
|
||||||
|
|
||||||
const ENTRY_COMPONENTS = [
|
const ENTRY_COMPONENTS = [
|
||||||
// put only entry components that use custom decorator
|
// put only entry components that use custom decorator
|
||||||
WorkspaceItemSearchResultListElementComponent,
|
WorkspaceItemSearchResultListElementComponent,
|
||||||
WorkflowItemSearchResultListElementComponent,
|
WorkflowItemSearchResultListElementComponent,
|
||||||
ClaimedSearchResultListElementComponent,
|
ClaimedSearchResultListElementComponent,
|
||||||
|
ClaimedApprovedSearchResultListElementComponent,
|
||||||
|
ClaimedDeclinedSearchResultListElementComponent,
|
||||||
PoolSearchResultListElementComponent,
|
PoolSearchResultListElementComponent,
|
||||||
ItemSearchResultDetailElementComponent,
|
ItemSearchResultDetailElementComponent,
|
||||||
WorkspaceItemSearchResultDetailElementComponent,
|
WorkspaceItemSearchResultDetailElementComponent,
|
||||||
|
@@ -8,9 +8,16 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv
|
|||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
import { ClaimedTaskDataService } from './claimed-task-data.service';
|
import { ClaimedTaskDataService } from './claimed-task-data.service';
|
||||||
|
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||||
|
import { FindListOptions } from '../data/request.models';
|
||||||
|
import { RequestParam } from '../cache/models/request-param.model';
|
||||||
|
import { getTestScheduler } from 'jasmine-marbles';
|
||||||
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||||
|
|
||||||
describe('ClaimedTaskDataService', () => {
|
describe('ClaimedTaskDataService', () => {
|
||||||
|
let scheduler: TestScheduler;
|
||||||
let service: ClaimedTaskDataService;
|
let service: ClaimedTaskDataService;
|
||||||
let options: HttpOptions;
|
let options: HttpOptions;
|
||||||
const taskEndpoint = 'https://rest.api/task';
|
const taskEndpoint = 'https://rest.api/task';
|
||||||
@@ -45,6 +52,7 @@ describe('ClaimedTaskDataService', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
scheduler = getTestScheduler();
|
||||||
service = initTestService();
|
service = initTestService();
|
||||||
options = Object.create({});
|
options = Object.create({});
|
||||||
let headers = new HttpHeaders();
|
let headers = new HttpHeaders();
|
||||||
@@ -68,6 +76,24 @@ describe('ClaimedTaskDataService', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('claimTask', () => {
|
||||||
|
|
||||||
|
it('should call postToEndpoint method', () => {
|
||||||
|
|
||||||
|
spyOn(service, 'postToEndpoint').and.returnValue(observableOf(null));
|
||||||
|
|
||||||
|
scheduler.schedule(() => service.claimTask('scopeId', 'poolTaskHref').subscribe());
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
const postToEndpointOptions: HttpOptions = Object.create({});
|
||||||
|
let headers = new HttpHeaders();
|
||||||
|
headers = headers.append('Content-Type', 'text/uri-list');
|
||||||
|
postToEndpointOptions.headers = headers;
|
||||||
|
|
||||||
|
expect(service.postToEndpoint).toHaveBeenCalledWith(linkPath, 'poolTaskHref', null, postToEndpointOptions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('returnToPoolTask', () => {
|
describe('returnToPoolTask', () => {
|
||||||
it('should call deleteById method', () => {
|
it('should call deleteById method', () => {
|
||||||
const scopeId = '1234';
|
const scopeId = '1234';
|
||||||
@@ -79,4 +105,21 @@ describe('ClaimedTaskDataService', () => {
|
|||||||
expect(service.deleteById).toHaveBeenCalledWith(linkPath, scopeId, options);
|
expect(service.deleteById).toHaveBeenCalledWith(linkPath, scopeId, options);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('findByItem', () => {
|
||||||
|
|
||||||
|
it('should call searchTask method', () => {
|
||||||
|
spyOn((service as any), 'searchTask').and.returnValue(observableOf(createSuccessfulRemoteDataObject$({})));
|
||||||
|
|
||||||
|
scheduler.schedule(() => service.findByItem('a0db0fde-1d12-4d43-bd0d-0f43df8d823c').subscribe());
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
const findListOptions = new FindListOptions();
|
||||||
|
findListOptions.searchParams = [
|
||||||
|
new RequestParam('uuid', 'a0db0fde-1d12-4d43-bd0d-0f43df8d823c')
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(service.searchTask).toHaveBeenCalledWith('findByItem', findListOptions);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
@@ -15,6 +15,11 @@ import { ClaimedTask } from './models/claimed-task-object.model';
|
|||||||
import { CLAIMED_TASK } from './models/claimed-task-object.resource-type';
|
import { CLAIMED_TASK } from './models/claimed-task-object.resource-type';
|
||||||
import { ProcessTaskResponse } from './models/process-task-response';
|
import { ProcessTaskResponse } from './models/process-task-response';
|
||||||
import { TasksService } from './tasks.service';
|
import { TasksService } from './tasks.service';
|
||||||
|
import { RemoteData } from '../data/remote-data';
|
||||||
|
import { FindListOptions } from '../data/request.models';
|
||||||
|
import { RequestParam } from '../cache/models/request-param.model';
|
||||||
|
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
||||||
|
import { getFirstSucceededRemoteData } from '../shared/operators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The service handling all REST requests for ClaimedTask
|
* The service handling all REST requests for ClaimedTask
|
||||||
@@ -23,7 +28,7 @@ import { TasksService } from './tasks.service';
|
|||||||
@dataService(CLAIMED_TASK)
|
@dataService(CLAIMED_TASK)
|
||||||
export class ClaimedTaskDataService extends TasksService<ClaimedTask> {
|
export class ClaimedTaskDataService extends TasksService<ClaimedTask> {
|
||||||
|
|
||||||
protected responseMsToLive = 10 * 1000;
|
protected responseMsToLive = 1000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The endpoint link name
|
* The endpoint link name
|
||||||
@@ -54,6 +59,24 @@ export class ClaimedTaskDataService extends TasksService<ClaimedTask> {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a request to claim the given task
|
||||||
|
*
|
||||||
|
* @param scopeId
|
||||||
|
* The task id
|
||||||
|
* @param poolTaskHref
|
||||||
|
* The pool task Href
|
||||||
|
* @return {Observable<ProcessTaskResponse>}
|
||||||
|
* Emit the server response
|
||||||
|
*/
|
||||||
|
public claimTask(scopeId: string, poolTaskHref: string): Observable<ProcessTaskResponse> {
|
||||||
|
const options: HttpOptions = Object.create({});
|
||||||
|
let headers = new HttpHeaders();
|
||||||
|
headers = headers.append('Content-Type', 'text/uri-list');
|
||||||
|
options.headers = headers;
|
||||||
|
return this.postToEndpoint(this.linkPath, poolTaskHref, null, options);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a request for the given task
|
* Make a request for the given task
|
||||||
*
|
*
|
||||||
@@ -80,4 +103,19 @@ export class ClaimedTaskDataService extends TasksService<ClaimedTask> {
|
|||||||
return this.deleteById(this.linkPath, scopeId, this.makeHttpOptions());
|
return this.deleteById(this.linkPath, scopeId, this.makeHttpOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search a claimed task by item uuid.
|
||||||
|
* @param uuid
|
||||||
|
* The item uuid
|
||||||
|
* @return {Observable<RemoteData<ClaimedTask>>}
|
||||||
|
* The server response
|
||||||
|
*/
|
||||||
|
public findByItem(uuid: string): Observable<RemoteData<ClaimedTask>> {
|
||||||
|
const options = new FindListOptions();
|
||||||
|
options.searchParams = [
|
||||||
|
new RequestParam('uuid', uuid)
|
||||||
|
];
|
||||||
|
return this.searchTask('findByItem', options).pipe(getFirstSucceededRemoteData());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -8,9 +8,16 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv
|
|||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
import { PoolTaskDataService } from './pool-task-data.service';
|
import { PoolTaskDataService } from './pool-task-data.service';
|
||||||
|
import { getTestScheduler } from 'jasmine-marbles';
|
||||||
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
|
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||||
|
import { FindListOptions } from '../data/request.models';
|
||||||
|
import { RequestParam } from '../cache/models/request-param.model';
|
||||||
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||||
|
|
||||||
describe('PoolTaskDataService', () => {
|
describe('PoolTaskDataService', () => {
|
||||||
|
let scheduler: TestScheduler;
|
||||||
let service: PoolTaskDataService;
|
let service: PoolTaskDataService;
|
||||||
let options: HttpOptions;
|
let options: HttpOptions;
|
||||||
const taskEndpoint = 'https://rest.api/task';
|
const taskEndpoint = 'https://rest.api/task';
|
||||||
@@ -45,6 +52,7 @@ describe('PoolTaskDataService', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
scheduler = getTestScheduler();
|
||||||
service = initTestService();
|
service = initTestService();
|
||||||
options = Object.create({});
|
options = Object.create({});
|
||||||
let headers = new HttpHeaders();
|
let headers = new HttpHeaders();
|
||||||
@@ -52,14 +60,33 @@ describe('PoolTaskDataService', () => {
|
|||||||
options.headers = headers;
|
options.headers = headers;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('claimTask', () => {
|
describe('findByItem', () => {
|
||||||
|
|
||||||
it('should call postToEndpoint method', () => {
|
it('should call searchTask method', () => {
|
||||||
spyOn(service, 'postToEndpoint');
|
spyOn((service as any), 'searchTask').and.returnValue(observableOf(createSuccessfulRemoteDataObject$({})));
|
||||||
const scopeId = '1234';
|
|
||||||
service.claimTask(scopeId);
|
scheduler.schedule(() => service.findByItem('a0db0fde-1d12-4d43-bd0d-0f43df8d823c').subscribe());
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
const findListOptions = new FindListOptions();
|
||||||
|
findListOptions.searchParams = [
|
||||||
|
new RequestParam('uuid', 'a0db0fde-1d12-4d43-bd0d-0f43df8d823c')
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(service.searchTask).toHaveBeenCalledWith('findByItem', findListOptions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getPoolTaskEndpointById', () => {
|
||||||
|
|
||||||
|
it('should call getEndpointById method', () => {
|
||||||
|
spyOn(service, 'getEndpointById').and.returnValue(observableOf(null));
|
||||||
|
|
||||||
|
scheduler.schedule(() => service.getPoolTaskEndpointById('a0db0fde-1d12-4d43-bd0d-0f43df8d823c').subscribe());
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect(service.getEndpointById).toHaveBeenCalledWith('a0db0fde-1d12-4d43-bd0d-0f43df8d823c');
|
||||||
|
|
||||||
expect(service.postToEndpoint).toHaveBeenCalledWith(linkPath, {}, scopeId, options);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -13,8 +13,11 @@ import { RequestService } from '../data/request.service';
|
|||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { PoolTask } from './models/pool-task-object.model';
|
import { PoolTask } from './models/pool-task-object.model';
|
||||||
import { POOL_TASK } from './models/pool-task-object.resource-type';
|
import { POOL_TASK } from './models/pool-task-object.resource-type';
|
||||||
import { ProcessTaskResponse } from './models/process-task-response';
|
|
||||||
import { TasksService } from './tasks.service';
|
import { TasksService } from './tasks.service';
|
||||||
|
import { RemoteData } from '../data/remote-data';
|
||||||
|
import { FindListOptions } from '../data/request.models';
|
||||||
|
import { RequestParam } from '../cache/models/request-param.model';
|
||||||
|
import { getFirstCompletedRemoteData } from '../shared/operators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The service handling all REST requests for PoolTask
|
* The service handling all REST requests for PoolTask
|
||||||
@@ -28,7 +31,7 @@ export class PoolTaskDataService extends TasksService<PoolTask> {
|
|||||||
*/
|
*/
|
||||||
protected linkPath = 'pooltasks';
|
protected linkPath = 'pooltasks';
|
||||||
|
|
||||||
protected responseMsToLive = 10 * 1000;
|
protected responseMsToLive = 1000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize instance variables
|
* Initialize instance variables
|
||||||
@@ -56,14 +59,30 @@ export class PoolTaskDataService extends TasksService<PoolTask> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a request to claim the given task
|
* Search a pool task by item uuid.
|
||||||
*
|
* @param uuid
|
||||||
* @param scopeId
|
* The item uuid
|
||||||
* The task id
|
* @return {Observable<RemoteData<ClaimedTask>>}
|
||||||
* @return {Observable<ProcessTaskResponse>}
|
* The server response
|
||||||
* Emit the server response
|
|
||||||
*/
|
*/
|
||||||
public claimTask(scopeId: string): Observable<ProcessTaskResponse> {
|
public findByItem(uuid: string): Observable<RemoteData<PoolTask>> {
|
||||||
return this.postToEndpoint(this.linkPath, {}, scopeId, this.makeHttpOptions());
|
const options = new FindListOptions();
|
||||||
|
options.searchParams = [
|
||||||
|
new RequestParam('uuid', uuid)
|
||||||
|
];
|
||||||
|
return this.searchTask('findByItem', options).pipe(getFirstCompletedRemoteData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Href of the pool task
|
||||||
|
*
|
||||||
|
* @param poolTaskId
|
||||||
|
* the poolTask id
|
||||||
|
* @return {Observable<string>>}
|
||||||
|
* the Href
|
||||||
|
*/
|
||||||
|
public getPoolTaskEndpointById(poolTaskId): Observable<string> {
|
||||||
|
return this.getEndpointById(poolTaskId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@ import { TestScheduler } from 'rxjs/testing';
|
|||||||
import { getMockRequestService } from '../../shared/mocks/request.service.mock';
|
import { getMockRequestService } from '../../shared/mocks/request.service.mock';
|
||||||
import { TasksService } from './tasks.service';
|
import { TasksService } from './tasks.service';
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { TaskDeleteRequest, TaskPostRequest } from '../data/request.models';
|
import { FindListOptions, TaskDeleteRequest, TaskPostRequest } from '../data/request.models';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub';
|
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub';
|
||||||
import { TaskObject } from './models/task-object.model';
|
import { TaskObject } from './models/task-object.model';
|
||||||
@@ -17,8 +17,11 @@ import { HttpClient, HttpHeaders } from '@angular/common/http';
|
|||||||
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
|
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
|
||||||
import { ChangeAnalyzer } from '../data/change-analyzer';
|
import { ChangeAnalyzer } from '../data/change-analyzer';
|
||||||
import { compare, Operation } from 'fast-json-patch';
|
import { compare, Operation } from 'fast-json-patch';
|
||||||
|
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||||
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
||||||
import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock';
|
import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock';
|
||||||
|
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
|
||||||
const LINK_NAME = 'test';
|
const LINK_NAME = 'test';
|
||||||
|
|
||||||
@@ -59,7 +62,7 @@ describe('TasksService', () => {
|
|||||||
const requestService = getMockRequestService();
|
const requestService = getMockRequestService();
|
||||||
const halService: any = new HALEndpointServiceStub(taskEndpoint);
|
const halService: any = new HALEndpointServiceStub(taskEndpoint);
|
||||||
const rdbService = getMockRemoteDataBuildService();
|
const rdbService = getMockRemoteDataBuildService();
|
||||||
const notificationsService = {} as NotificationsService;
|
const notificationsService = new NotificationsServiceStub() as any;
|
||||||
const http = {} as HttpClient;
|
const http = {} as HttpClient;
|
||||||
const comparator = new DummyChangeAnalyzer() as any;
|
const comparator = new DummyChangeAnalyzer() as any;
|
||||||
const objectCache = {
|
const objectCache = {
|
||||||
@@ -118,4 +121,38 @@ describe('TasksService', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('searchTask', () => {
|
||||||
|
|
||||||
|
it('should call findByHref with the href generated by getSearchByHref', () => {
|
||||||
|
|
||||||
|
spyOn(service, 'getSearchByHref').and.returnValue(observableOf('generatedHref'));
|
||||||
|
spyOn(service, 'findByHref').and.returnValue(of(null));
|
||||||
|
|
||||||
|
const followLinks = {};
|
||||||
|
const options = new FindListOptions();
|
||||||
|
options.searchParams = [];
|
||||||
|
|
||||||
|
scheduler.schedule(() => service.searchTask('method', options, followLinks as any).subscribe());
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect(service.getSearchByHref).toHaveBeenCalledWith('method', options, followLinks as any);
|
||||||
|
expect(service.findByHref).toHaveBeenCalledWith('generatedHref', false, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getEndpointById', () => {
|
||||||
|
|
||||||
|
it('should call halService.getEndpoint and then getEndpointByIDHref', () => {
|
||||||
|
|
||||||
|
spyOn(halService, 'getEndpoint').and.returnValue(observableOf('generatedHref'));
|
||||||
|
spyOn(service, 'getEndpointByIDHref').and.returnValue(null);
|
||||||
|
|
||||||
|
scheduler.schedule(() => service.getEndpointById('scopeId').subscribe());
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect(halService.getEndpoint).toHaveBeenCalledWith(service.getLinkPath());
|
||||||
|
expect(service.getEndpointByIDHref).toHaveBeenCalledWith('generatedHref', 'scopeId');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -1,43 +1,29 @@
|
|||||||
import { HttpHeaders } from '@angular/common/http';
|
import { HttpHeaders } from '@angular/common/http';
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { distinctUntilChanged, filter, map, mergeMap, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, find, map, mergeMap, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
import { DataService } from '../data/data.service';
|
import { DataService } from '../data/data.service';
|
||||||
import { DeleteRequest, PostRequest, TaskDeleteRequest, TaskPostRequest } from '../data/request.models';
|
import {
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
DeleteRequest,
|
||||||
|
FindListOptions,
|
||||||
|
PostRequest,
|
||||||
|
TaskDeleteRequest,
|
||||||
|
TaskPostRequest
|
||||||
|
} from '../data/request.models';
|
||||||
|
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
||||||
import { ProcessTaskResponse } from './models/process-task-response';
|
import { ProcessTaskResponse } from './models/process-task-response';
|
||||||
import { getFirstCompletedRemoteData } from '../shared/operators';
|
import { getAllCompletedRemoteData, getFirstCompletedRemoteData } from '../shared/operators';
|
||||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||||
import { RemoteData } from '../data/remote-data';
|
import { RemoteData } from '../data/remote-data';
|
||||||
|
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract class that provides methods to handle task requests.
|
* An abstract class that provides methods to handle task requests.
|
||||||
*/
|
*/
|
||||||
export abstract class TasksService<T extends CacheableObject> extends DataService<T> {
|
export abstract class TasksService<T extends CacheableObject> extends DataService<T> {
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch a RestRequest
|
|
||||||
*
|
|
||||||
* @param requestId
|
|
||||||
* The base endpoint for the type of object
|
|
||||||
* @return Observable<ProcessTaskResponse>
|
|
||||||
* server response
|
|
||||||
*/
|
|
||||||
protected fetchRequest(requestId: string): Observable<ProcessTaskResponse> {
|
|
||||||
return this.rdbService.buildFromRequestUUID(requestId).pipe(
|
|
||||||
getFirstCompletedRemoteData(),
|
|
||||||
map((response: RemoteData<any>) => {
|
|
||||||
if (response.hasFailed) {
|
|
||||||
return new ProcessTaskResponse(false, response.statusCode, response.errorMessage);
|
|
||||||
} else {
|
|
||||||
return new ProcessTaskResponse(true, response.statusCode);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the HREF for a specific submission object based on its identifier
|
* Create the HREF for a specific submission object based on its identifier
|
||||||
*
|
*
|
||||||
@@ -46,7 +32,7 @@ export abstract class TasksService<T extends CacheableObject> extends DataServic
|
|||||||
* @param resourceID
|
* @param resourceID
|
||||||
* The identifier for the object
|
* The identifier for the object
|
||||||
*/
|
*/
|
||||||
protected getEndpointByIDHref(endpoint, resourceID): string {
|
getEndpointByIDHref(endpoint, resourceID): string {
|
||||||
return isNotEmpty(resourceID) ? `${endpoint}/${resourceID}` : `${endpoint}`;
|
return isNotEmpty(resourceID) ? `${endpoint}/${resourceID}` : `${endpoint}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,16 +76,67 @@ export abstract class TasksService<T extends CacheableObject> extends DataServic
|
|||||||
*/
|
*/
|
||||||
public deleteById(linkPath: string, scopeId: string, options?: HttpOptions): Observable<ProcessTaskResponse> {
|
public deleteById(linkPath: string, scopeId: string, options?: HttpOptions): Observable<ProcessTaskResponse> {
|
||||||
const requestId = this.requestService.generateRequestId();
|
const requestId = this.requestService.generateRequestId();
|
||||||
return this.halService.getEndpoint(linkPath || this.linkPath).pipe(
|
return this.getEndpointById(scopeId, linkPath).pipe(
|
||||||
filter((href: string) => isNotEmpty(href)),
|
|
||||||
distinctUntilChanged(),
|
|
||||||
map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, scopeId)),
|
|
||||||
map((endpointURL: string) => new TaskDeleteRequest(requestId, endpointURL, null, options)),
|
map((endpointURL: string) => new TaskDeleteRequest(requestId, endpointURL, null, options)),
|
||||||
tap((request: DeleteRequest) => this.requestService.send(request)),
|
tap((request: DeleteRequest) => this.requestService.send(request)),
|
||||||
mergeMap((request: DeleteRequest) => this.fetchRequest(requestId)),
|
mergeMap((request: DeleteRequest) => this.fetchRequest(requestId)),
|
||||||
distinctUntilChanged());
|
distinctUntilChanged());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the endpoint of a task by scopeId.
|
||||||
|
* @param linkPath
|
||||||
|
* @param scopeId
|
||||||
|
*/
|
||||||
|
public getEndpointById(scopeId: string, linkPath?: string): Observable<string> {
|
||||||
|
return this.halService.getEndpoint(linkPath || this.linkPath).pipe(
|
||||||
|
filter((href: string) => isNotEmpty(href)),
|
||||||
|
distinctUntilChanged(),
|
||||||
|
map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, scopeId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search a task.
|
||||||
|
* @param searchMethod
|
||||||
|
* the search method
|
||||||
|
* @param options
|
||||||
|
* the find list options
|
||||||
|
* @param linksToFollow
|
||||||
|
* links to follow
|
||||||
|
*/
|
||||||
|
public searchTask(searchMethod: string, options: FindListOptions = {}, ...linksToFollow: FollowLinkConfig<T>[]): Observable<RemoteData<T>> {
|
||||||
|
const hrefObs = this.getSearchByHref(searchMethod, options, ...linksToFollow);
|
||||||
|
return hrefObs.pipe(
|
||||||
|
find((href: string) => hasValue(href)),
|
||||||
|
mergeMap((href) => this.findByHref(href, false, true).pipe(
|
||||||
|
getAllCompletedRemoteData(),
|
||||||
|
filter((rd: RemoteData<T>) => !rd.isSuccessStale),
|
||||||
|
tap(() => this.requestService.setStaleByHrefSubstring(href)))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a RestRequest
|
||||||
|
*
|
||||||
|
* @param requestId
|
||||||
|
* The base endpoint for the type of object
|
||||||
|
* @return Observable<ProcessTaskResponse>
|
||||||
|
* server response
|
||||||
|
*/
|
||||||
|
protected fetchRequest(requestId: string): Observable<ProcessTaskResponse> {
|
||||||
|
return this.rdbService.buildFromRequestUUID(requestId).pipe(
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
map((response: RemoteData<any>) => {
|
||||||
|
if (response.hasFailed) {
|
||||||
|
return new ProcessTaskResponse(false, response.statusCode, response.errorMessage);
|
||||||
|
} else {
|
||||||
|
return new ProcessTaskResponse(true, response.statusCode);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new HttpOptions
|
* Create a new HttpOptions
|
||||||
*/
|
*/
|
||||||
|
@@ -1,8 +1,20 @@
|
|||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
import { Component, Injector, OnDestroy } from '@angular/core';
|
||||||
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
|
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
|
||||||
import { BehaviorSubject } from 'rxjs';
|
|
||||||
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
|
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
|
||||||
import { ProcessTaskResponse } from '../../../../core/tasks/models/process-task-response';
|
import { DSpaceObject} from '../../../../core/shared/dspace-object.model';
|
||||||
|
import { Router} from '@angular/router';
|
||||||
|
import { NotificationsService} from '../../../notifications/notifications.service';
|
||||||
|
import { TranslateService} from '@ngx-translate/core';
|
||||||
|
import { SearchService} from '../../../../core/shared/search/search.service';
|
||||||
|
import { RequestService} from '../../../../core/data/request.service';
|
||||||
|
import { Observable} from 'rxjs';
|
||||||
|
import { RemoteData} from '../../../../core/data/remote-data';
|
||||||
|
import { WorkflowItem} from '../../../../core/submission/models/workflowitem.model';
|
||||||
|
import { switchMap, take } from 'rxjs/operators';
|
||||||
|
import { CLAIMED_TASK } from '../../../../core/tasks/models/claimed-task-object.resource-type';
|
||||||
|
import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators';
|
||||||
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
|
import { MyDSpaceReloadableActionsComponent } from '../../mydspace-reloadable-actions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract component for rendering a claimed task's action
|
* Abstract component for rendering a claimed task's action
|
||||||
@@ -12,31 +24,39 @@ import { ProcessTaskResponse } from '../../../../core/tasks/models/process-task-
|
|||||||
* - Optionally overwrite createBody if the request body requires more than just the option
|
* - Optionally overwrite createBody if the request body requires more than just the option
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-calim-task-action-abstract',
|
selector: 'ds-claimed-task-action-abstract',
|
||||||
template: ''
|
template: ''
|
||||||
})
|
})
|
||||||
export abstract class ClaimedTaskActionsAbstractComponent {
|
export abstract class ClaimedTaskActionsAbstractComponent extends MyDSpaceReloadableActionsComponent<ClaimedTask, ClaimedTaskDataService> implements OnDestroy {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The workflow task option the child component represents
|
* The workflow task option the child component represents
|
||||||
*/
|
*/
|
||||||
abstract option: string;
|
abstract option: string;
|
||||||
|
|
||||||
/**
|
object: ClaimedTask;
|
||||||
* The Claimed Task to display an action for
|
|
||||||
*/
|
|
||||||
@Input() object: ClaimedTask;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emits the success or failure of a processed action
|
* Anchor used to reload the pool task.
|
||||||
*/
|
*/
|
||||||
@Output() processCompleted: EventEmitter<boolean> = new EventEmitter<boolean>();
|
itemUuid: string;
|
||||||
|
|
||||||
|
subs = [];
|
||||||
|
|
||||||
|
protected constructor(protected injector: Injector,
|
||||||
|
protected router: Router,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected translate: TranslateService,
|
||||||
|
protected searchService: SearchService,
|
||||||
|
protected requestService: RequestService) {
|
||||||
|
super(CLAIMED_TASK, injector, router, notificationsService, translate, searchService, requestService);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A boolean representing if the operation is pending
|
* Submit the action on the claimed object.
|
||||||
*/
|
*/
|
||||||
processing$ = new BehaviorSubject<boolean>(false);
|
submitTask() {
|
||||||
|
this.subs.push(this.startActionExecution().pipe(take(1)).subscribe());
|
||||||
constructor(protected claimedTaskService: ClaimedTaskDataService) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,17 +69,36 @@ export abstract class ClaimedTaskActionsAbstractComponent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
reloadObjectExecution(): Observable<RemoteData<DSpaceObject> | DSpaceObject> {
|
||||||
* Submit the task for this option
|
return this.objectDataService.findByItem(this.itemUuid as string);
|
||||||
* While the task is submitting, processing$ is set to true and processCompleted emits the response's status when
|
|
||||||
* completed
|
|
||||||
*/
|
|
||||||
submitTask() {
|
|
||||||
this.processing$.next(true);
|
|
||||||
this.claimedTaskService.submitTask(this.object.id, this.createbody())
|
|
||||||
.subscribe((res: ProcessTaskResponse) => {
|
|
||||||
this.processing$.next(false);
|
|
||||||
this.processCompleted.emit(res.hasSucceeded);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actionExecution(): Observable<any> {
|
||||||
|
return this.objectDataService.submitTask(this.object.id, this.createbody());
|
||||||
|
}
|
||||||
|
|
||||||
|
initObjects(object: ClaimedTask) {
|
||||||
|
this.object = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the itemUuid.
|
||||||
|
*/
|
||||||
|
initReloadAnchor() {
|
||||||
|
if (!(this.object as any).workflowitem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.subs.push(this.object.workflowitem.pipe(
|
||||||
|
getFirstSucceededRemoteDataPayload(),
|
||||||
|
switchMap((workflowItem: WorkflowItem) => workflowItem.item.pipe(getFirstSucceededRemoteDataPayload())
|
||||||
|
))
|
||||||
|
.subscribe((item: Item) => {
|
||||||
|
this.itemUuid = item.uuid;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.subs.forEach((sub) => sub.unsubscribe());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,18 +1,33 @@
|
|||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of, of as observableOf } from 'rxjs';
|
||||||
|
|
||||||
import { ClaimedTaskActionsApproveComponent } from './claimed-task-actions-approve.component';
|
import { ClaimedTaskActionsApproveComponent } from './claimed-task-actions-approve.component';
|
||||||
import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock';
|
import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock';
|
||||||
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
|
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
|
||||||
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
|
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
|
||||||
import { ProcessTaskResponse } from '../../../../core/tasks/models/process-task-response';
|
import { ProcessTaskResponse } from '../../../../core/tasks/models/process-task-response';
|
||||||
|
import { getMockSearchService } from '../../../mocks/search-service.mock';
|
||||||
|
import { getMockRequestService } from '../../../mocks/request.service.mock';
|
||||||
|
import { PoolTaskDataService } from '../../../../core/tasks/pool-task-data.service';
|
||||||
|
import { NotificationsService } from '../../../notifications/notifications.service';
|
||||||
|
import { NotificationsServiceStub } from '../../../testing/notifications-service.stub';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { RouterStub } from '../../../testing/router.stub';
|
||||||
|
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||||
|
import { RequestService } from '../../../../core/data/request.service';
|
||||||
|
|
||||||
let component: ClaimedTaskActionsApproveComponent;
|
let component: ClaimedTaskActionsApproveComponent;
|
||||||
let fixture: ComponentFixture<ClaimedTaskActionsApproveComponent>;
|
let fixture: ComponentFixture<ClaimedTaskActionsApproveComponent>;
|
||||||
|
|
||||||
|
const searchService = getMockSearchService();
|
||||||
|
|
||||||
|
const requestService = getMockRequestService();
|
||||||
|
|
||||||
|
let mockPoolTaskDataService: PoolTaskDataService;
|
||||||
|
|
||||||
describe('ClaimedTaskActionsApproveComponent', () => {
|
describe('ClaimedTaskActionsApproveComponent', () => {
|
||||||
const object = Object.assign(new ClaimedTask(), { id: 'claimed-task-1' });
|
const object = Object.assign(new ClaimedTask(), { id: 'claimed-task-1' });
|
||||||
const claimedTaskService = jasmine.createSpyObj('claimedTaskService', {
|
const claimedTaskService = jasmine.createSpyObj('claimedTaskService', {
|
||||||
@@ -20,6 +35,7 @@ describe('ClaimedTaskActionsApproveComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
|
mockPoolTaskDataService = new PoolTaskDataService(null, null, null, null, null, null, null, null);
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
TranslateModule.forRoot({
|
TranslateModule.forRoot({
|
||||||
@@ -30,7 +46,13 @@ describe('ClaimedTaskActionsApproveComponent', () => {
|
|||||||
})
|
})
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ClaimedTaskDataService, useValue: claimedTaskService }
|
{ provide: ClaimedTaskDataService, useValue: claimedTaskService },
|
||||||
|
{ provide: Injector, useValue: {} },
|
||||||
|
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||||
|
{ provide: Router, useValue: new RouterStub() },
|
||||||
|
{ provide: SearchService, useValue: searchService },
|
||||||
|
{ provide: RequestService, useValue: requestService },
|
||||||
|
{ provide: PoolTaskDataService, useValue: mockPoolTaskDataService },
|
||||||
],
|
],
|
||||||
declarations: [ClaimedTaskActionsApproveComponent],
|
declarations: [ClaimedTaskActionsApproveComponent],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
@@ -43,6 +65,7 @@ describe('ClaimedTaskActionsApproveComponent', () => {
|
|||||||
fixture = TestBed.createComponent(ClaimedTaskActionsApproveComponent);
|
fixture = TestBed.createComponent(ClaimedTaskActionsApproveComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
component.object = object;
|
component.object = object;
|
||||||
|
spyOn(component, 'initReloadAnchor').and.returnValue(undefined);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -66,6 +89,7 @@ describe('ClaimedTaskActionsApproveComponent', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(component.processCompleted, 'emit');
|
spyOn(component.processCompleted, 'emit');
|
||||||
|
spyOn(component, 'startActionExecution').and.returnValue(of(null));
|
||||||
|
|
||||||
expectedBody = {
|
expectedBody = {
|
||||||
[component.option]: 'true'
|
[component.option]: 'true'
|
||||||
@@ -75,12 +99,34 @@ describe('ClaimedTaskActionsApproveComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call claimedTaskService\'s submitTask with the expected body', () => {
|
it('should start the action execution', () => {
|
||||||
expect(claimedTaskService.submitTask).toHaveBeenCalledWith(object.id, expectedBody);
|
expect(component.startActionExecution).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('actionExecution', () => {
|
||||||
|
|
||||||
|
it('should call claimedTaskService\'s submitTask', (done) => {
|
||||||
|
|
||||||
|
const expectedBody = {
|
||||||
|
[component.option]: 'true'
|
||||||
|
};
|
||||||
|
|
||||||
|
component.actionExecution().subscribe(() => {
|
||||||
|
expect(claimedTaskService.submitTask).toHaveBeenCalledWith(object.id, expectedBody);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit a successful processCompleted event', () => {
|
});
|
||||||
expect(component.processCompleted.emit).toHaveBeenCalledWith(true);
|
|
||||||
|
describe('reloadObjectExecution', () => {
|
||||||
|
|
||||||
|
it('should return the component object itself', (done) => {
|
||||||
|
component.reloadObjectExecution().subscribe((val) => {
|
||||||
|
expect(val).toEqual(component.object);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,7 +1,16 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, Injector } from '@angular/core';
|
||||||
import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component';
|
import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component';
|
||||||
import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator';
|
import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator';
|
||||||
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
|
import { Observable } from 'rxjs';
|
||||||
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
|
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { NotificationsService } from '../../../notifications/notifications.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||||
|
import { RequestService } from '../../../../core/data/request.service';
|
||||||
|
import { ClaimedApprovedTaskSearchResult } from '../../../object-collection/shared/claimed-approved-task-search-result.model';
|
||||||
|
import { of } from 'rxjs/internal/observable/of';
|
||||||
|
|
||||||
export const WORKFLOW_TASK_OPTION_APPROVE = 'submit_approve';
|
export const WORKFLOW_TASK_OPTION_APPROVE = 'submit_approve';
|
||||||
|
|
||||||
@@ -20,7 +29,24 @@ export class ClaimedTaskActionsApproveComponent extends ClaimedTaskActionsAbstra
|
|||||||
*/
|
*/
|
||||||
option = WORKFLOW_TASK_OPTION_APPROVE;
|
option = WORKFLOW_TASK_OPTION_APPROVE;
|
||||||
|
|
||||||
constructor(protected claimedTaskService: ClaimedTaskDataService) {
|
constructor(protected injector: Injector,
|
||||||
super(claimedTaskService);
|
protected router: Router,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected translate: TranslateService,
|
||||||
|
protected searchService: SearchService,
|
||||||
|
protected requestService: RequestService) {
|
||||||
|
super(injector, router, notificationsService, translate, searchService, requestService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reloadObjectExecution(): Observable<RemoteData<DSpaceObject> | DSpaceObject> {
|
||||||
|
return of(this.object);
|
||||||
|
}
|
||||||
|
|
||||||
|
convertReloadedObject(dso: DSpaceObject): DSpaceObject {
|
||||||
|
const reloadedObject = Object.assign(new ClaimedApprovedTaskSearchResult(), dso, {
|
||||||
|
indexableObject: dso
|
||||||
|
});
|
||||||
|
return reloadedObject;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -3,11 +3,11 @@
|
|||||||
<ds-claimed-task-actions-loader *ngFor="let option of workflowAction?.options"
|
<ds-claimed-task-actions-loader *ngFor="let option of workflowAction?.options"
|
||||||
[option]="option"
|
[option]="option"
|
||||||
[object]="object"
|
[object]="object"
|
||||||
(processCompleted)="handleActionResponse($event)">
|
(processCompleted)="this.processCompleted.emit($event)">
|
||||||
</ds-claimed-task-actions-loader>
|
</ds-claimed-task-actions-loader>
|
||||||
<ds-claimed-task-actions-loader [option]="returnToPoolOption"
|
<ds-claimed-task-actions-loader [option]="returnToPoolOption"
|
||||||
[object]="object"
|
[object]="object"
|
||||||
(processCompleted)="handleActionResponse($event)">
|
(processCompleted)="this.processCompleted.emit($event)">
|
||||||
</ds-claimed-task-actions-loader>
|
</ds-claimed-task-actions-loader>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@@ -2,7 +2,7 @@ import { Component, Injector, Input, OnInit } from '@angular/core';
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { filter, map } from 'rxjs/operators';
|
import { filter, map, take } from 'rxjs/operators';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service';
|
import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service';
|
||||||
@@ -87,7 +87,8 @@ export class ClaimedTaskActionsComponent extends MyDSpaceActionsComponent<Claime
|
|||||||
this.object = object;
|
this.object = object;
|
||||||
this.workflowitem$ = (this.object.workflowitem as Observable<RemoteData<WorkflowItem>>).pipe(
|
this.workflowitem$ = (this.object.workflowitem as Observable<RemoteData<WorkflowItem>>).pipe(
|
||||||
filter((rd: RemoteData<WorkflowItem>) => ((!rd.isRequestPending) && isNotUndefined(rd.payload))),
|
filter((rd: RemoteData<WorkflowItem>) => ((!rd.isRequestPending) && isNotUndefined(rd.payload))),
|
||||||
map((rd: RemoteData<WorkflowItem>) => rd.payload));
|
map((rd: RemoteData<WorkflowItem>) => rd.payload),
|
||||||
|
take(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<a *ngIf="object"
|
<a *ngIf="object"
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
ngbTooltip="{{'submission.workflow.tasks.claimed.edit_help' | translate}}"
|
ngbTooltip="{{'submission.workflow.tasks.claimed.edit_help' | translate}}"
|
||||||
[routerLink]="['/workflowitems/' + (object.workflowitem | async)?.payload.id + '/edit']"
|
[routerLink]="['/workflowitems/' + (object.workflowitem | async)?.payload?.id + '/edit']"
|
||||||
role="button">
|
role="button">
|
||||||
<i class="fa fa-edit"></i> {{'submission.workflow.tasks.claimed.edit' | translate}}
|
<i class="fa fa-edit"></i> {{'submission.workflow.tasks.claimed.edit' | translate}}
|
||||||
</a>
|
</a>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
@@ -7,13 +7,28 @@ import { ClaimedTaskActionsEditMetadataComponent } from './claimed-task-actions-
|
|||||||
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
|
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
|
||||||
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
|
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
|
||||||
import { TranslateLoaderMock } from '../../../testing/translate-loader.mock';
|
import { TranslateLoaderMock } from '../../../testing/translate-loader.mock';
|
||||||
|
import { getMockSearchService } from '../../../mocks/search-service.mock';
|
||||||
|
import { getMockRequestService } from '../../../mocks/request.service.mock';
|
||||||
|
import { PoolTaskDataService } from '../../../../core/tasks/pool-task-data.service';
|
||||||
|
import { NotificationsService } from '../../../notifications/notifications.service';
|
||||||
|
import { NotificationsServiceStub } from '../../../testing/notifications-service.stub';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { RouterStub } from '../../../testing/router.stub';
|
||||||
|
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||||
|
import { RequestService } from '../../../../core/data/request.service';
|
||||||
|
|
||||||
let component: ClaimedTaskActionsEditMetadataComponent;
|
let component: ClaimedTaskActionsEditMetadataComponent;
|
||||||
let fixture: ComponentFixture<ClaimedTaskActionsEditMetadataComponent>;
|
let fixture: ComponentFixture<ClaimedTaskActionsEditMetadataComponent>;
|
||||||
|
|
||||||
|
const searchService = getMockSearchService();
|
||||||
|
|
||||||
|
const requestService = getMockRequestService();
|
||||||
|
|
||||||
|
let mockPoolTaskDataService: PoolTaskDataService;
|
||||||
|
|
||||||
describe('ClaimedTaskActionsEditMetadataComponent', () => {
|
describe('ClaimedTaskActionsEditMetadataComponent', () => {
|
||||||
const object = Object.assign(new ClaimedTask(), { id: 'claimed-task-1' });
|
const object = Object.assign(new ClaimedTask(), { id: 'claimed-task-1' });
|
||||||
|
mockPoolTaskDataService = new PoolTaskDataService(null, null, null, null, null, null, null, null);
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -25,7 +40,13 @@ describe('ClaimedTaskActionsEditMetadataComponent', () => {
|
|||||||
})
|
})
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ClaimedTaskDataService, useValue: {} }
|
{ provide: ClaimedTaskDataService, useValue: {} },
|
||||||
|
{ provide: Injector, useValue: {} },
|
||||||
|
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||||
|
{ provide: Router, useValue: new RouterStub() },
|
||||||
|
{ provide: SearchService, useValue: searchService },
|
||||||
|
{ provide: RequestService, useValue: requestService },
|
||||||
|
{ provide: PoolTaskDataService, useValue: mockPoolTaskDataService },
|
||||||
],
|
],
|
||||||
declarations: [ClaimedTaskActionsEditMetadataComponent],
|
declarations: [ClaimedTaskActionsEditMetadataComponent],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
@@ -38,6 +59,7 @@ describe('ClaimedTaskActionsEditMetadataComponent', () => {
|
|||||||
fixture = TestBed.createComponent(ClaimedTaskActionsEditMetadataComponent);
|
fixture = TestBed.createComponent(ClaimedTaskActionsEditMetadataComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
component.object = object;
|
component.object = object;
|
||||||
|
spyOn(component, 'initReloadAnchor').and.returnValue(undefined);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,7 +1,11 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, Injector } from '@angular/core';
|
||||||
import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component';
|
import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component';
|
||||||
import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator';
|
import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator';
|
||||||
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
|
import { Router } from '@angular/router';
|
||||||
|
import { NotificationsService } from '../../../notifications/notifications.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||||
|
import { RequestService } from '../../../../core/data/request.service';
|
||||||
|
|
||||||
export const WORKFLOW_TASK_OPTION_EDIT_METADATA = 'submit_edit_metadata';
|
export const WORKFLOW_TASK_OPTION_EDIT_METADATA = 'submit_edit_metadata';
|
||||||
|
|
||||||
@@ -20,7 +24,12 @@ export class ClaimedTaskActionsEditMetadataComponent extends ClaimedTaskActionsA
|
|||||||
*/
|
*/
|
||||||
option = WORKFLOW_TASK_OPTION_EDIT_METADATA;
|
option = WORKFLOW_TASK_OPTION_EDIT_METADATA;
|
||||||
|
|
||||||
constructor(protected claimedTaskService: ClaimedTaskDataService) {
|
constructor(protected injector: Injector,
|
||||||
super(claimedTaskService);
|
protected router: Router,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected translate: TranslateService,
|
||||||
|
protected searchService: SearchService,
|
||||||
|
protected requestService: RequestService) {
|
||||||
|
super(injector, router, notificationsService, translate, searchService, requestService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
@@ -9,22 +9,39 @@ import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
|||||||
import { ClaimedTaskActionsRejectComponent } from './claimed-task-actions-reject.component';
|
import { ClaimedTaskActionsRejectComponent } from './claimed-task-actions-reject.component';
|
||||||
import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock';
|
import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock';
|
||||||
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
|
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
|
||||||
import { of as observableOf } from 'rxjs';
|
|
||||||
import { ProcessTaskResponse } from '../../../../core/tasks/models/process-task-response';
|
import { ProcessTaskResponse } from '../../../../core/tasks/models/process-task-response';
|
||||||
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
|
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
|
||||||
|
import { NotificationsService } from '../../../notifications/notifications.service';
|
||||||
|
import { NotificationsServiceStub } from '../../../testing/notifications-service.stub';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { RouterStub } from '../../../testing/router.stub';
|
||||||
|
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||||
|
import { RequestService } from '../../../../core/data/request.service';
|
||||||
|
import { PoolTaskDataService } from '../../../../core/tasks/pool-task-data.service';
|
||||||
|
import { getMockSearchService } from '../../../mocks/search-service.mock';
|
||||||
|
import { getMockRequestService } from '../../../mocks/request.service.mock';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { ClaimedDeclinedTaskSearchResult } from '../../../object-collection/shared/claimed-declined-task-search-result.model';
|
||||||
|
|
||||||
let component: ClaimedTaskActionsRejectComponent;
|
let component: ClaimedTaskActionsRejectComponent;
|
||||||
let fixture: ComponentFixture<ClaimedTaskActionsRejectComponent>;
|
let fixture: ComponentFixture<ClaimedTaskActionsRejectComponent>;
|
||||||
let formBuilder: FormBuilder;
|
let formBuilder: FormBuilder;
|
||||||
let modalService: NgbModal;
|
let modalService: NgbModal;
|
||||||
|
|
||||||
|
const searchService = getMockSearchService();
|
||||||
|
|
||||||
|
const requestService = getMockRequestService();
|
||||||
|
|
||||||
|
let mockPoolTaskDataService: PoolTaskDataService;
|
||||||
|
|
||||||
describe('ClaimedTaskActionsRejectComponent', () => {
|
describe('ClaimedTaskActionsRejectComponent', () => {
|
||||||
const object = Object.assign(new ClaimedTask(), { id: 'claimed-task-1' });
|
const object = Object.assign(new ClaimedTask(), { id: 'claimed-task-1' });
|
||||||
const claimedTaskService = jasmine.createSpyObj('claimedTaskService', {
|
const claimedTaskService = jasmine.createSpyObj('claimedTaskService', {
|
||||||
submitTask: observableOf(new ProcessTaskResponse(true))
|
submitTask: of(new ProcessTaskResponse(true))
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
|
mockPoolTaskDataService = new PoolTaskDataService(null, null, null, null, null, null, null, null);
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
NgbModule,
|
NgbModule,
|
||||||
@@ -39,6 +56,12 @@ describe('ClaimedTaskActionsRejectComponent', () => {
|
|||||||
declarations: [ClaimedTaskActionsRejectComponent],
|
declarations: [ClaimedTaskActionsRejectComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ClaimedTaskDataService, useValue: claimedTaskService },
|
{ provide: ClaimedTaskDataService, useValue: claimedTaskService },
|
||||||
|
{ provide: Injector, useValue: {} },
|
||||||
|
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||||
|
{ provide: Router, useValue: new RouterStub() },
|
||||||
|
{ provide: SearchService, useValue: searchService },
|
||||||
|
{ provide: RequestService, useValue: requestService },
|
||||||
|
{ provide: PoolTaskDataService, useValue: mockPoolTaskDataService },
|
||||||
FormBuilder,
|
FormBuilder,
|
||||||
NgbModal
|
NgbModal
|
||||||
],
|
],
|
||||||
@@ -55,6 +78,7 @@ describe('ClaimedTaskActionsRejectComponent', () => {
|
|||||||
modalService = TestBed.inject(NgbModal);
|
modalService = TestBed.inject(NgbModal);
|
||||||
component.object = object;
|
component.object = object;
|
||||||
component.modalRef = modalService.open('ok');
|
component.modalRef = modalService.open('ok');
|
||||||
|
spyOn(component, 'initReloadAnchor').and.returnValue(undefined);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -96,6 +120,7 @@ describe('ClaimedTaskActionsRejectComponent', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(component.processCompleted, 'emit');
|
spyOn(component.processCompleted, 'emit');
|
||||||
|
spyOn(component, 'startActionExecution').and.returnValue(of(null));
|
||||||
|
|
||||||
expectedBody = {
|
expectedBody = {
|
||||||
[component.option]: 'true',
|
[component.option]: 'true',
|
||||||
@@ -113,12 +138,48 @@ describe('ClaimedTaskActionsRejectComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call claimedTaskService\'s submitTask with the expected body', () => {
|
it('should start the action execution', () => {
|
||||||
expect(claimedTaskService.submitTask).toHaveBeenCalledWith(object.id, expectedBody);
|
expect(component.startActionExecution).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit a successful processCompleted event', () => {
|
});
|
||||||
expect(component.processCompleted.emit).toHaveBeenCalledWith(true);
|
|
||||||
|
describe('actionExecution', () => {
|
||||||
|
|
||||||
|
let expectedBody;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn((component.rejectForm as any), 'get').and.returnValue({value: 'required'});
|
||||||
|
expectedBody = {
|
||||||
|
[component.option]: 'true',
|
||||||
|
reason: 'required'
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call claimedTaskService\'s submitTask with the proper reason', (done) => {
|
||||||
|
component.actionExecution().subscribe(() => {
|
||||||
|
expect(claimedTaskService.submitTask).toHaveBeenCalledWith(object.id, expectedBody);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('reloadObjectExecution', () => {
|
||||||
|
|
||||||
|
it('should return the component object itself', (done) => {
|
||||||
|
component.reloadObjectExecution().subscribe((val) => {
|
||||||
|
expect(val).toEqual(component.object);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('convertReloadedObject', () => {
|
||||||
|
|
||||||
|
it('should return a ClaimedDeclinedTaskSearchResult instance', () => {
|
||||||
|
const reloadedObject = component.convertReloadedObject(component.object);
|
||||||
|
expect(reloadedObject instanceof ClaimedDeclinedTaskSearchResult).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -1,10 +1,19 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, Injector, OnInit } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
|
||||||
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component';
|
import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component';
|
||||||
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
|
|
||||||
import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator';
|
import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { NotificationsService } from '../../../notifications/notifications.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||||
|
import { RequestService } from '../../../../core/data/request.service';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
|
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
||||||
|
import { of } from 'rxjs/internal/observable/of';
|
||||||
|
import { ClaimedDeclinedTaskSearchResult } from '../../../object-collection/shared/claimed-declined-task-search-result.model';
|
||||||
|
|
||||||
export const WORKFLOW_TASK_OPTION_REJECT = 'submit_reject';
|
export const WORKFLOW_TASK_OPTION_REJECT = 'submit_reject';
|
||||||
|
|
||||||
@@ -33,17 +42,15 @@ export class ClaimedTaskActionsRejectComponent extends ClaimedTaskActionsAbstrac
|
|||||||
*/
|
*/
|
||||||
public modalRef: NgbModalRef;
|
public modalRef: NgbModalRef;
|
||||||
|
|
||||||
/**
|
constructor(protected injector: Injector,
|
||||||
* Initialize instance variables
|
protected router: Router,
|
||||||
*
|
protected notificationsService: NotificationsService,
|
||||||
* @param {FormBuilder} formBuilder
|
protected translate: TranslateService,
|
||||||
* @param {NgbModal} modalService
|
protected searchService: SearchService,
|
||||||
* @param claimedTaskService
|
protected requestService: RequestService,
|
||||||
*/
|
|
||||||
constructor(protected claimedTaskService: ClaimedTaskDataService,
|
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private modalService: NgbModal) {
|
private modalService: NgbModal) {
|
||||||
super(claimedTaskService);
|
super(injector, router, notificationsService, translate, searchService, requestService);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,6 +62,14 @@ export class ClaimedTaskActionsRejectComponent extends ClaimedTaskActionsAbstrac
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit a reject option for the task
|
||||||
|
*/
|
||||||
|
submitTask() {
|
||||||
|
this.modalRef.close('Send Button');
|
||||||
|
super.submitTask();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the request body for rejecting a workflow task
|
* Create the request body for rejecting a workflow task
|
||||||
* Includes the reason from the form
|
* Includes the reason from the form
|
||||||
@@ -64,14 +79,6 @@ export class ClaimedTaskActionsRejectComponent extends ClaimedTaskActionsAbstrac
|
|||||||
return Object.assign(super.createbody(), { reason });
|
return Object.assign(super.createbody(), { reason });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Submit a reject option for the task
|
|
||||||
*/
|
|
||||||
submitTask() {
|
|
||||||
this.modalRef.close('Send Button');
|
|
||||||
super.submitTask();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open modal
|
* Open modal
|
||||||
*
|
*
|
||||||
@@ -81,4 +88,15 @@ export class ClaimedTaskActionsRejectComponent extends ClaimedTaskActionsAbstrac
|
|||||||
this.rejectForm.reset();
|
this.rejectForm.reset();
|
||||||
this.modalRef = this.modalService.open(content);
|
this.modalRef = this.modalService.open(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reloadObjectExecution(): Observable<RemoteData<DSpaceObject> | DSpaceObject> {
|
||||||
|
return of(this.object);
|
||||||
|
}
|
||||||
|
|
||||||
|
convertReloadedObject(dso: DSpaceObject): DSpaceObject {
|
||||||
|
const reloadedObject = Object.assign(new ClaimedDeclinedTaskSearchResult(), dso, {
|
||||||
|
indexableObject: dso
|
||||||
|
});
|
||||||
|
return reloadedObject;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,25 +1,41 @@
|
|||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { ClaimedTaskActionsReturnToPoolComponent } from './claimed-task-actions-return-to-pool.component';
|
import { ClaimedTaskActionsReturnToPoolComponent } from './claimed-task-actions-return-to-pool.component';
|
||||||
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
|
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
|
||||||
import { of as observableOf } from 'rxjs';
|
|
||||||
import { ProcessTaskResponse } from '../../../../core/tasks/models/process-task-response';
|
import { ProcessTaskResponse } from '../../../../core/tasks/models/process-task-response';
|
||||||
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
|
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
|
||||||
import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock';
|
import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock';
|
||||||
|
import { NotificationsService } from '../../../notifications/notifications.service';
|
||||||
|
import { NotificationsServiceStub } from '../../../testing/notifications-service.stub';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { RouterStub } from '../../../testing/router.stub';
|
||||||
|
import { PoolTaskDataService } from '../../../../core/tasks/pool-task-data.service';
|
||||||
|
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||||
|
import { RequestService } from '../../../../core/data/request.service';
|
||||||
|
import { getMockSearchService } from '../../../mocks/search-service.mock';
|
||||||
|
import { getMockRequestService } from '../../../mocks/request.service.mock';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
|
||||||
let component: ClaimedTaskActionsReturnToPoolComponent;
|
let component: ClaimedTaskActionsReturnToPoolComponent;
|
||||||
let fixture: ComponentFixture<ClaimedTaskActionsReturnToPoolComponent>;
|
let fixture: ComponentFixture<ClaimedTaskActionsReturnToPoolComponent>;
|
||||||
|
|
||||||
|
const searchService = getMockSearchService();
|
||||||
|
|
||||||
|
const requestService = getMockRequestService();
|
||||||
|
|
||||||
|
let mockPoolTaskDataService: PoolTaskDataService;
|
||||||
|
|
||||||
describe('ClaimedTaskActionsReturnToPoolComponent', () => {
|
describe('ClaimedTaskActionsReturnToPoolComponent', () => {
|
||||||
const object = Object.assign(new ClaimedTask(), { id: 'claimed-task-1' });
|
const object = Object.assign(new ClaimedTask(), { id: 'claimed-task-1' });
|
||||||
const claimedTaskService = jasmine.createSpyObj('claimedTaskService', {
|
const claimedTaskService = jasmine.createSpyObj('claimedTaskService', {
|
||||||
returnToPoolTask: observableOf(new ProcessTaskResponse(true))
|
returnToPoolTask: of(new ProcessTaskResponse(true))
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
|
mockPoolTaskDataService = new PoolTaskDataService(null, null, null, null, null, null, null, null);
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
TranslateModule.forRoot({
|
TranslateModule.forRoot({
|
||||||
@@ -30,7 +46,13 @@ describe('ClaimedTaskActionsReturnToPoolComponent', () => {
|
|||||||
})
|
})
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ClaimedTaskDataService, useValue: claimedTaskService }
|
{ provide: ClaimedTaskDataService, useValue: claimedTaskService },
|
||||||
|
{ provide: Injector, useValue: {} },
|
||||||
|
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||||
|
{ provide: Router, useValue: new RouterStub() },
|
||||||
|
{ provide: SearchService, useValue: searchService },
|
||||||
|
{ provide: RequestService, useValue: requestService },
|
||||||
|
{ provide: PoolTaskDataService, useValue: mockPoolTaskDataService },
|
||||||
],
|
],
|
||||||
declarations: [ClaimedTaskActionsReturnToPoolComponent],
|
declarations: [ClaimedTaskActionsReturnToPoolComponent],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
@@ -39,12 +61,13 @@ describe('ClaimedTaskActionsReturnToPoolComponent', () => {
|
|||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(fakeAsync(() => {
|
||||||
fixture = TestBed.createComponent(ClaimedTaskActionsReturnToPoolComponent);
|
fixture = TestBed.createComponent(ClaimedTaskActionsReturnToPoolComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
component.object = object;
|
component.object = object;
|
||||||
|
spyOn(component, 'initReloadAnchor').and.returnValue(undefined);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
}));
|
||||||
|
|
||||||
it('should display return to pool button', () => {
|
it('should display return to pool button', () => {
|
||||||
const btn = fixture.debugElement.query(By.css('.btn-secondary'));
|
const btn = fixture.debugElement.query(By.css('.btn-secondary'));
|
||||||
@@ -61,11 +84,9 @@ describe('ClaimedTaskActionsReturnToPoolComponent', () => {
|
|||||||
expect(span).toBeDefined();
|
expect(span).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('submitTask', () => {
|
describe('actionExecution', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(component.processCompleted, 'emit');
|
component.actionExecution().subscribe();
|
||||||
|
|
||||||
component.submitTask();
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -73,8 +94,19 @@ describe('ClaimedTaskActionsReturnToPoolComponent', () => {
|
|||||||
expect(claimedTaskService.returnToPoolTask).toHaveBeenCalledWith(object.id);
|
expect(claimedTaskService.returnToPoolTask).toHaveBeenCalledWith(object.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit a successful processCompleted event', () => {
|
});
|
||||||
expect(component.processCompleted.emit).toHaveBeenCalledWith(true);
|
|
||||||
|
describe('reloadObjectExecution', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(mockPoolTaskDataService, 'findByItem').and.returnValue(of(null));
|
||||||
|
|
||||||
|
component.itemUuid = 'uuid';
|
||||||
|
component.reloadObjectExecution().subscribe();
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call poolTaskDataService findItem with itemUuid', () => {
|
||||||
|
expect(mockPoolTaskDataService.findByItem).toHaveBeenCalledWith('uuid');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,8 +1,16 @@
|
|||||||
import { Component } from '@angular/core';
|
import {Component, Injector} from '@angular/core';
|
||||||
import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component';
|
import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component';
|
||||||
import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator';
|
import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator';
|
||||||
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
|
import { Observable } from 'rxjs';
|
||||||
import { ProcessTaskResponse } from '../../../../core/tasks/models/process-task-response';
|
import { Router } from '@angular/router';
|
||||||
|
import { NotificationsService } from '../../../notifications/notifications.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||||
|
import { RequestService } from '../../../../core/data/request.service';
|
||||||
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
|
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
||||||
|
import { PoolTaskDataService } from '../../../../core/tasks/pool-task-data.service';
|
||||||
|
import { take } from 'rxjs/operators';
|
||||||
|
|
||||||
export const WORKFLOW_TASK_OPTION_RETURN_TO_POOL = 'return_to_pool';
|
export const WORKFLOW_TASK_OPTION_RETURN_TO_POOL = 'return_to_pool';
|
||||||
|
|
||||||
@@ -21,19 +29,22 @@ export class ClaimedTaskActionsReturnToPoolComponent extends ClaimedTaskActionsA
|
|||||||
*/
|
*/
|
||||||
option = WORKFLOW_TASK_OPTION_RETURN_TO_POOL;
|
option = WORKFLOW_TASK_OPTION_RETURN_TO_POOL;
|
||||||
|
|
||||||
constructor(protected claimedTaskService: ClaimedTaskDataService) {
|
constructor(protected injector: Injector,
|
||||||
super(claimedTaskService);
|
protected router: Router,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected translate: TranslateService,
|
||||||
|
protected searchService: SearchService,
|
||||||
|
protected requestService: RequestService,
|
||||||
|
private poolTaskService: PoolTaskDataService) {
|
||||||
|
super(injector, router, notificationsService, translate, searchService, requestService);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
reloadObjectExecution(): Observable<RemoteData<DSpaceObject> | DSpaceObject> {
|
||||||
* Submit a return to pool option for the task
|
return this.poolTaskService.findByItem(this.itemUuid).pipe(take(1));
|
||||||
*/
|
|
||||||
submitTask() {
|
|
||||||
this.processing$.next(true);
|
|
||||||
this.claimedTaskService.returnToPoolTask(this.object.id)
|
|
||||||
.subscribe((res: ProcessTaskResponse) => {
|
|
||||||
this.processing$.next(false);
|
|
||||||
this.processCompleted.emit(res.hasSucceeded);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actionExecution(): Observable<any> {
|
||||||
|
return this.objectDataService.returnToPoolTask(this.object.id);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,15 +1,26 @@
|
|||||||
import { ClaimedTaskActionsLoaderComponent } from './claimed-task-actions-loader.component';
|
import { ClaimedTaskActionsLoaderComponent } from './claimed-task-actions-loader.component';
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { ChangeDetectionStrategy, ComponentFactoryResolver, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import * as decorators from './claimed-task-actions-decorator';
|
|
||||||
import { ClaimedTaskActionsDirective } from './claimed-task-actions.directive';
|
import { ClaimedTaskActionsDirective } from './claimed-task-actions.directive';
|
||||||
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
|
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { ClaimedTaskActionsEditMetadataComponent } from '../edit-metadata/claimed-task-actions-edit-metadata.component';
|
import { ClaimedTaskActionsEditMetadataComponent } from '../edit-metadata/claimed-task-actions-edit-metadata.component';
|
||||||
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
|
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
|
||||||
import { spyOnExported } from '../../../testing/utils.test';
|
import { NotificationsService } from '../../../notifications/notifications.service';
|
||||||
|
import { NotificationsServiceStub } from '../../../testing/notifications-service.stub';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { RouterStub } from '../../../testing/router.stub';
|
||||||
|
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||||
|
import { RequestService } from '../../../../core/data/request.service';
|
||||||
|
import { PoolTaskDataService } from '../../../../core/tasks/pool-task-data.service';
|
||||||
|
import { getMockSearchService } from '../../../mocks/search-service.mock';
|
||||||
|
import { getMockRequestService } from '../../../mocks/request.service.mock';
|
||||||
|
|
||||||
xdescribe('ClaimedTaskActionsLoaderComponent', () => {
|
const searchService = getMockSearchService();
|
||||||
|
|
||||||
|
const requestService = getMockRequestService();
|
||||||
|
|
||||||
|
describe('ClaimedTaskActionsLoaderComponent', () => {
|
||||||
let comp: ClaimedTaskActionsLoaderComponent;
|
let comp: ClaimedTaskActionsLoaderComponent;
|
||||||
let fixture: ComponentFixture<ClaimedTaskActionsLoaderComponent>;
|
let fixture: ComponentFixture<ClaimedTaskActionsLoaderComponent>;
|
||||||
|
|
||||||
@@ -23,7 +34,12 @@ xdescribe('ClaimedTaskActionsLoaderComponent', () => {
|
|||||||
schemas: [NO_ERRORS_SCHEMA],
|
schemas: [NO_ERRORS_SCHEMA],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ClaimedTaskDataService, useValue: {} },
|
{ provide: ClaimedTaskDataService, useValue: {} },
|
||||||
ComponentFactoryResolver
|
{ provide: Injector, useValue: {} },
|
||||||
|
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||||
|
{ provide: Router, useValue: new RouterStub() },
|
||||||
|
{ provide: SearchService, useValue: searchService },
|
||||||
|
{ provide: RequestService, useValue: requestService },
|
||||||
|
{ provide: PoolTaskDataService, useValue: {} }
|
||||||
]
|
]
|
||||||
}).overrideComponent(ClaimedTaskActionsLoaderComponent, {
|
}).overrideComponent(ClaimedTaskActionsLoaderComponent, {
|
||||||
set: {
|
set: {
|
||||||
@@ -36,16 +52,16 @@ xdescribe('ClaimedTaskActionsLoaderComponent', () => {
|
|||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
fixture = TestBed.createComponent(ClaimedTaskActionsLoaderComponent);
|
fixture = TestBed.createComponent(ClaimedTaskActionsLoaderComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
|
|
||||||
comp.object = object;
|
comp.object = object;
|
||||||
comp.option = option;
|
comp.option = option;
|
||||||
spyOnExported(decorators, 'getComponentByWorkflowTaskOption').and.returnValue(ClaimedTaskActionsEditMetadataComponent);
|
spyOn(comp, 'getComponentByWorkflowTaskOption').and.returnValue(ClaimedTaskActionsEditMetadataComponent);
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('When the component is rendered', () => {
|
describe('When the component is rendered', () => {
|
||||||
it('should call the getComponentByWorkflowTaskOption function with the right option', () => {
|
it('should call the getComponentByWorkflowTaskOption function with the right option', () => {
|
||||||
expect(decorators.getComponentByWorkflowTaskOption).toHaveBeenCalledWith(option);
|
expect(comp.getComponentByWorkflowTaskOption).toHaveBeenCalledWith(option);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -14,6 +14,7 @@ import { ClaimedTaskActionsDirective } from './claimed-task-actions.directive';
|
|||||||
import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component';
|
import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component';
|
||||||
import { hasValue } from '../../../empty.util';
|
import { hasValue } from '../../../empty.util';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
import { MyDSpaceActionsResult } from '../../mydspace-actions';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-claimed-task-actions-loader',
|
selector: 'ds-claimed-task-actions-loader',
|
||||||
@@ -38,7 +39,7 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnDestroy {
|
|||||||
/**
|
/**
|
||||||
* Emits the success or failure of a processed action
|
* Emits the success or failure of a processed action
|
||||||
*/
|
*/
|
||||||
@Output() processCompleted: EventEmitter<boolean> = new EventEmitter<boolean>();
|
@Output() processCompleted = new EventEmitter<MyDSpaceActionsResult>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directive to determine where the dynamic child component is located
|
* Directive to determine where the dynamic child component is located
|
||||||
@@ -58,7 +59,8 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnDestroy {
|
|||||||
* Fetch, create and initialize the relevant component
|
* Fetch, create and initialize the relevant component
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
const comp = getComponentByWorkflowTaskOption(this.option);
|
|
||||||
|
const comp = this.getComponentByWorkflowTaskOption(this.option);
|
||||||
if (hasValue(comp)) {
|
if (hasValue(comp)) {
|
||||||
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(comp);
|
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(comp);
|
||||||
|
|
||||||
@@ -69,11 +71,15 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnDestroy {
|
|||||||
const componentInstance = (componentRef.instance as ClaimedTaskActionsAbstractComponent);
|
const componentInstance = (componentRef.instance as ClaimedTaskActionsAbstractComponent);
|
||||||
componentInstance.object = this.object;
|
componentInstance.object = this.object;
|
||||||
if (hasValue(componentInstance.processCompleted)) {
|
if (hasValue(componentInstance.processCompleted)) {
|
||||||
this.subs.push(componentInstance.processCompleted.subscribe((success) => this.processCompleted.emit(success)));
|
this.subs.push(componentInstance.processCompleted.subscribe((result) => this.processCompleted.emit(result)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getComponentByWorkflowTaskOption(option: string) {
|
||||||
|
return getComponentByWorkflowTaskOption(option);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unsubscribe from open subscriptions
|
* Unsubscribe from open subscriptions
|
||||||
*/
|
*/
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { Component, Injector, Input } from '@angular/core';
|
import { Component, EventEmitter, Injector, Input, Output } from '@angular/core';
|
||||||
|
|
||||||
import { take, tap } from 'rxjs/operators';
|
import { take, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
@@ -12,10 +12,15 @@ import { NotificationOptions } from '../notifications/models/notification-option
|
|||||||
import { NotificationsService } from '../notifications/notifications.service';
|
import { NotificationsService } from '../notifications/notifications.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { RequestService } from '../../core/data/request.service';
|
import { RequestService } from '../../core/data/request.service';
|
||||||
import { Subscription } from 'rxjs';
|
import { BehaviorSubject, Subscription } from 'rxjs';
|
||||||
import { SearchService } from '../../core/shared/search/search.service';
|
import { SearchService } from '../../core/shared/search/search.service';
|
||||||
import { getFirstSucceededRemoteData } from '../../core/shared/operators';
|
import { getFirstSucceededRemoteData } from '../../core/shared/operators';
|
||||||
|
|
||||||
|
export interface MyDSpaceActionsResult {
|
||||||
|
result: boolean;
|
||||||
|
reloadedObject: DSpaceObject;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class for all different representations of mydspace actions
|
* Abstract class for all different representations of mydspace actions
|
||||||
*/
|
*/
|
||||||
@@ -31,7 +36,18 @@ export abstract class MyDSpaceActionsComponent<T extends DSpaceObject, TService
|
|||||||
@Input() abstract object: T;
|
@Input() abstract object: T;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instance of DataService realted to mydspace object
|
* Emit to notify the instantiator when the action has been performed.
|
||||||
|
*/
|
||||||
|
@Output() processCompleted = new EventEmitter<MyDSpaceActionsResult>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if an operation is pending
|
||||||
|
* @type {BehaviorSubject<boolean>}
|
||||||
|
*/
|
||||||
|
public processing$ = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance of DataService related to mydspace object
|
||||||
*/
|
*/
|
||||||
protected objectDataService: TService;
|
protected objectDataService: TService;
|
||||||
|
|
||||||
@@ -71,6 +87,7 @@ export abstract class MyDSpaceActionsComponent<T extends DSpaceObject, TService
|
|||||||
* Refresh current page
|
* Refresh current page
|
||||||
*/
|
*/
|
||||||
reload(): void {
|
reload(): void {
|
||||||
|
|
||||||
this.router.navigated = false;
|
this.router.navigated = false;
|
||||||
const url = decodeURIComponent(this.router.url);
|
const url = decodeURIComponent(this.router.url);
|
||||||
// override the route reuse strategy
|
// override the route reuse strategy
|
||||||
@@ -89,6 +106,7 @@ export abstract class MyDSpaceActionsComponent<T extends DSpaceObject, TService
|
|||||||
* Override the target object with a refreshed one
|
* Override the target object with a refreshed one
|
||||||
*/
|
*/
|
||||||
refresh(): void {
|
refresh(): void {
|
||||||
|
|
||||||
// find object by id
|
// find object by id
|
||||||
this.objectDataService.findById(this.object.id, false).pipe(
|
this.objectDataService.findById(this.object.id, false).pipe(
|
||||||
getFirstSucceededRemoteData(),
|
getFirstSucceededRemoteData(),
|
||||||
|
@@ -0,0 +1,260 @@
|
|||||||
|
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import {ComponentFixture, fakeAsync, TestBed} from '@angular/core/testing';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { PoolTaskDataService } from '../../core/tasks/pool-task-data.service';
|
||||||
|
import { ClaimedTaskDataService } from '../../core/tasks/claimed-task-data.service';
|
||||||
|
import { PoolTaskActionsComponent } from './pool-task/pool-task-actions.component';
|
||||||
|
import { PoolTask } from '../../core/tasks/models/pool-task-object.model';
|
||||||
|
import { NotificationsServiceStub } from '../testing/notifications-service.stub';
|
||||||
|
import { RouterStub } from '../testing/router.stub';
|
||||||
|
import { getMockSearchService } from '../mocks/search-service.mock';
|
||||||
|
import { getMockRequestService } from '../mocks/request.service.mock';
|
||||||
|
import { Item } from '../../core/shared/item.model';
|
||||||
|
import { createFailedRemoteDataObject, createSuccessfulRemoteDataObject } from '../remote-data.utils';
|
||||||
|
import { WorkflowItem } from '../../core/submission/models/workflowitem.model';
|
||||||
|
import { TranslateLoaderMock } from '../mocks/translate-loader.mock';
|
||||||
|
import { NotificationsService } from '../notifications/notifications.service';
|
||||||
|
import { SearchService } from '../../core/shared/search/search.service';
|
||||||
|
import { RequestService } from '../../core/data/request.service';
|
||||||
|
import { ProcessTaskResponse } from '../../core/tasks/models/process-task-response';
|
||||||
|
|
||||||
|
let mockDataService: PoolTaskDataService;
|
||||||
|
let mockClaimedTaskDataService: ClaimedTaskDataService;
|
||||||
|
|
||||||
|
let component: PoolTaskActionsComponent;
|
||||||
|
let fixture: ComponentFixture<PoolTaskActionsComponent>;
|
||||||
|
|
||||||
|
let mockObject: PoolTask;
|
||||||
|
let notificationsServiceStub: NotificationsServiceStub;
|
||||||
|
let router: RouterStub;
|
||||||
|
|
||||||
|
const searchService = getMockSearchService();
|
||||||
|
|
||||||
|
const requestService = getMockRequestService();
|
||||||
|
|
||||||
|
const item = Object.assign(new Item(), {
|
||||||
|
bundles: observableOf({}),
|
||||||
|
metadata: {
|
||||||
|
'dc.title': [
|
||||||
|
{
|
||||||
|
language: 'en_US',
|
||||||
|
value: 'This is just another title'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'dc.type': [
|
||||||
|
{
|
||||||
|
language: null,
|
||||||
|
value: 'Article'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'dc.contributor.author': [
|
||||||
|
{
|
||||||
|
language: 'en_US',
|
||||||
|
value: 'Smith, Donald'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'dc.date.issued': [
|
||||||
|
{
|
||||||
|
language: null,
|
||||||
|
value: '2015-06-26'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const rdItem = createSuccessfulRemoteDataObject(item);
|
||||||
|
const workflowitem = Object.assign(new WorkflowItem(), { item: observableOf(rdItem) });
|
||||||
|
const rdWorkflowitem = createSuccessfulRemoteDataObject(workflowitem);
|
||||||
|
mockObject = Object.assign(new PoolTask(), { workflowitem: observableOf(rdWorkflowitem), id: '1234' });
|
||||||
|
|
||||||
|
describe('MyDSpaceReloadableActionsComponent', () => {
|
||||||
|
beforeEach(fakeAsync(() => {
|
||||||
|
mockDataService = new PoolTaskDataService(null, null, null, null, null, null, null, null);
|
||||||
|
mockClaimedTaskDataService = new ClaimedTaskDataService(null, null, null, null, null, null, null, null);
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot({
|
||||||
|
loader: {
|
||||||
|
provide: TranslateLoader,
|
||||||
|
useClass: TranslateLoaderMock
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
declarations: [PoolTaskActionsComponent],
|
||||||
|
providers: [
|
||||||
|
{ provide: Injector, useValue: {} },
|
||||||
|
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||||
|
{ provide: Router, useValue: new RouterStub() },
|
||||||
|
{ provide: PoolTaskDataService, useValue: mockDataService },
|
||||||
|
{ provide: ClaimedTaskDataService, useValue: mockClaimedTaskDataService },
|
||||||
|
{ provide: SearchService, useValue: searchService },
|
||||||
|
{ provide: RequestService, useValue: requestService }
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
}).overrideComponent(PoolTaskActionsComponent, {
|
||||||
|
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PoolTaskActionsComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.object = mockObject;
|
||||||
|
notificationsServiceStub = TestBed.get(NotificationsService);
|
||||||
|
router = TestBed.get(Router);
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fixture = null;
|
||||||
|
component = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('on reload action init', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(component, 'initReloadAnchor').and.returnValue(null);
|
||||||
|
spyOn(component, 'initObjects');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call initReloadAnchor and initObjects on init', fakeAsync(() => {
|
||||||
|
component.ngOnInit();
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(component.initReloadAnchor).toHaveBeenCalled();
|
||||||
|
expect(component.initObjects).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('on action execution fail', () => {
|
||||||
|
|
||||||
|
let remoteClaimTaskErrorResponse;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
|
||||||
|
mockDataService = new PoolTaskDataService(null, null, null, null, null, null, null, null);
|
||||||
|
|
||||||
|
const poolTaskHref = 'poolTaskHref';
|
||||||
|
remoteClaimTaskErrorResponse = new ProcessTaskResponse(false, null, null);
|
||||||
|
const remoteReloadedObjectResponse: any = createSuccessfulRemoteDataObject(new PoolTask());
|
||||||
|
|
||||||
|
spyOn(mockDataService, 'getPoolTaskEndpointById').and.returnValue(observableOf(poolTaskHref));
|
||||||
|
spyOn(mockClaimedTaskDataService, 'findByItem').and.returnValue(observableOf(remoteReloadedObjectResponse));
|
||||||
|
spyOn(mockClaimedTaskDataService, 'claimTask').and.returnValue(observableOf(remoteClaimTaskErrorResponse));
|
||||||
|
spyOn(component, 'reloadObjectExecution').and.callThrough();
|
||||||
|
spyOn(component.processCompleted, 'emit').and.callThrough();
|
||||||
|
|
||||||
|
(component as any).objectDataService = mockDataService;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show error notification', (done) => {
|
||||||
|
|
||||||
|
component.startActionExecution().subscribe( (result) => {
|
||||||
|
expect(notificationsServiceStub.error).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not call reloadObject', (done) => {
|
||||||
|
|
||||||
|
component.startActionExecution().subscribe( (result) => {
|
||||||
|
expect(component.reloadObjectExecution).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not emit processCompleted', (done) => {
|
||||||
|
|
||||||
|
component.startActionExecution().subscribe( (result) => {
|
||||||
|
expect(component.processCompleted.emit).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('on action execution success', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
|
||||||
|
mockDataService = new PoolTaskDataService(null, null, null, null, null, null, null, null);
|
||||||
|
|
||||||
|
const poolTaskHref = 'poolTaskHref';
|
||||||
|
const remoteClaimTaskResponse: any = new ProcessTaskResponse(true, null, null);
|
||||||
|
const remoteReloadedObjectResponse: any = createSuccessfulRemoteDataObject(new PoolTask());
|
||||||
|
|
||||||
|
spyOn(mockDataService, 'getPoolTaskEndpointById').and.returnValue(observableOf(poolTaskHref));
|
||||||
|
spyOn(mockClaimedTaskDataService, 'findByItem').and.returnValue(observableOf(remoteReloadedObjectResponse));
|
||||||
|
spyOn(mockClaimedTaskDataService, 'claimTask').and.returnValue(observableOf(remoteClaimTaskResponse));
|
||||||
|
spyOn(component, 'reloadObjectExecution').and.callThrough();
|
||||||
|
spyOn(component, 'convertReloadedObject').and.callThrough();
|
||||||
|
spyOn(component.processCompleted, 'emit').and.callThrough();
|
||||||
|
|
||||||
|
(component as any).objectDataService = mockDataService;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reloadObject in case of action execution success', (done) => {
|
||||||
|
|
||||||
|
component.startActionExecution().subscribe( (result) => {
|
||||||
|
expect(component.reloadObjectExecution).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert the reloaded object', (done) => {
|
||||||
|
|
||||||
|
component.startActionExecution().subscribe( (result) => {
|
||||||
|
expect(component.convertReloadedObject).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit the reloaded object in case of success', (done) => {
|
||||||
|
|
||||||
|
component.startActionExecution().subscribe( (result) => {
|
||||||
|
expect(component.processCompleted.emit).toHaveBeenCalledWith({result: true, reloadedObject: result as any});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('on action execution success but without a reloadedObject', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
|
||||||
|
mockDataService = new PoolTaskDataService(null, null, null, null, null, null, null, null);
|
||||||
|
|
||||||
|
const poolTaskHref = 'poolTaskHref';
|
||||||
|
const remoteClaimTaskResponse: any = new ProcessTaskResponse(true, null, null);
|
||||||
|
const remoteReloadedObjectResponse: any = createFailedRemoteDataObject();
|
||||||
|
|
||||||
|
spyOn(mockDataService, 'getPoolTaskEndpointById').and.returnValue(observableOf(poolTaskHref));
|
||||||
|
spyOn(mockClaimedTaskDataService, 'findByItem').and.returnValue(observableOf(remoteReloadedObjectResponse));
|
||||||
|
spyOn(mockClaimedTaskDataService, 'claimTask').and.returnValue(observableOf(remoteClaimTaskResponse));
|
||||||
|
|
||||||
|
spyOn(component, 'convertReloadedObject').and.returnValue(null);
|
||||||
|
spyOn(component, 'reload').and.returnValue(null);
|
||||||
|
|
||||||
|
(component as any).objectDataService = mockDataService;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call reload method', (done) => {
|
||||||
|
|
||||||
|
component.startActionExecution().subscribe( (result) => {
|
||||||
|
expect(component.reload).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
145
src/app/shared/mydspace-actions/mydspace-reloadable-actions.ts
Normal file
145
src/app/shared/mydspace-actions/mydspace-reloadable-actions.ts
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { Component, Injector, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
import { map, switchMap, take, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
|
import { DataService } from '../../core/data/data.service';
|
||||||
|
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||||
|
import { ResourceType } from '../../core/shared/resource-type';
|
||||||
|
import { NotificationOptions } from '../notifications/models/notification-options.model';
|
||||||
|
import { NotificationsService } from '../notifications/notifications.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { RequestService } from '../../core/data/request.service';
|
||||||
|
import { SearchService } from '../../core/shared/search/search.service';
|
||||||
|
import { Observable} from 'rxjs/internal/Observable';
|
||||||
|
import { of} from 'rxjs/internal/observable/of';
|
||||||
|
import { ProcessTaskResponse } from '../../core/tasks/models/process-task-response';
|
||||||
|
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
|
||||||
|
import { getSearchResultFor } from '../search/search-result-element-decorator';
|
||||||
|
import { MyDSpaceActionsComponent } from './mydspace-actions';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract class for all different representations of mydspace actions
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-mydspace-reloadable-actions',
|
||||||
|
template: ''
|
||||||
|
})
|
||||||
|
export abstract class MyDSpaceReloadableActionsComponent<T extends DSpaceObject, TService extends DataService<T>>
|
||||||
|
extends MyDSpaceActionsComponent<T, TService> implements OnInit {
|
||||||
|
|
||||||
|
protected constructor(
|
||||||
|
protected objectType: ResourceType,
|
||||||
|
protected injector: Injector,
|
||||||
|
protected router: Router,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected translate: TranslateService,
|
||||||
|
protected searchService: SearchService,
|
||||||
|
protected requestService: RequestService) {
|
||||||
|
super(objectType, injector, router, notificationsService, translate, searchService, requestService);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the actual implementation of this reloadable action.
|
||||||
|
*/
|
||||||
|
abstract actionExecution(): Observable<ProcessTaskResponse>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload the object (typically by a remote call).
|
||||||
|
*/
|
||||||
|
abstract reloadObjectExecution(): Observable<RemoteData<DSpaceObject> | DSpaceObject>;
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.initReloadAnchor();
|
||||||
|
this.initObjects(this.object);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the execution of the action.
|
||||||
|
* 1. performAction
|
||||||
|
* 2. reload of the object
|
||||||
|
* 3. notification
|
||||||
|
*/
|
||||||
|
startActionExecution(): Observable<DSpaceObject> {
|
||||||
|
this.processing$.next(true);
|
||||||
|
return this.actionExecution().pipe(
|
||||||
|
take(1),
|
||||||
|
switchMap((res: ProcessTaskResponse) => {
|
||||||
|
if (res.hasSucceeded) {
|
||||||
|
return this._reloadObject().pipe(
|
||||||
|
tap(
|
||||||
|
(reloadedObject) => {
|
||||||
|
this.processing$.next(false);
|
||||||
|
this.handleReloadableActionResponse(res.hasSucceeded, reloadedObject);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.processing$.next(false);
|
||||||
|
this.handleReloadableActionResponse(res.hasSucceeded, null);
|
||||||
|
return of(null);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the action response and show properly notifications.
|
||||||
|
*
|
||||||
|
* @param result
|
||||||
|
* true on success, false otherwise
|
||||||
|
* @param reloadedObject
|
||||||
|
* the reloadedObject
|
||||||
|
*/
|
||||||
|
handleReloadableActionResponse(result: boolean, reloadedObject: DSpaceObject): void {
|
||||||
|
if (result) {
|
||||||
|
if (reloadedObject) {
|
||||||
|
this.processCompleted.emit({result, reloadedObject});
|
||||||
|
} else {
|
||||||
|
this.reload();
|
||||||
|
}
|
||||||
|
this.notificationsService.success(null,
|
||||||
|
this.translate.get('submission.workflow.tasks.generic.success'),
|
||||||
|
new NotificationOptions(5000, false));
|
||||||
|
} else {
|
||||||
|
this.notificationsService.error(null,
|
||||||
|
this.translate.get('submission.workflow.tasks.generic.error'),
|
||||||
|
new NotificationOptions(20000, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook called on init to initialized the required information used to reload the object.
|
||||||
|
*/
|
||||||
|
// tslint:disable-next-line:no-empty
|
||||||
|
initReloadAnchor() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the reloadedObject to the Type required by this action.
|
||||||
|
* @param dso
|
||||||
|
*/
|
||||||
|
convertReloadedObject(dso: DSpaceObject): DSpaceObject {
|
||||||
|
const constructor = getSearchResultFor((dso as any).constructor);
|
||||||
|
const reloadedObject = Object.assign(new constructor(), dso, {
|
||||||
|
indexableObject: dso
|
||||||
|
});
|
||||||
|
return reloadedObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the refreshed object and transform it to a reloadedObject.
|
||||||
|
* @param dso
|
||||||
|
*/
|
||||||
|
private _reloadObject(): Observable<DSpaceObject> {
|
||||||
|
return this.reloadObjectExecution().pipe(
|
||||||
|
switchMap((res) => {
|
||||||
|
if (res instanceof RemoteData) {
|
||||||
|
return of(res).pipe(getFirstCompletedRemoteData(), map((completed) => completed.payload));
|
||||||
|
} else {
|
||||||
|
return of(res);
|
||||||
|
}
|
||||||
|
})).pipe(map((dso) => {
|
||||||
|
return dso ? this.convertReloadedObject(dso) : dso;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,8 +1,8 @@
|
|||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-info mt-1 mb-3"
|
class="btn btn-info mt-1 mb-3"
|
||||||
ngbTooltip="{{'submission.workflow.tasks.pool.claim_help' | translate}}"
|
ngbTooltip="{{'submission.workflow.tasks.pool.claim_help' | translate}}"
|
||||||
[disabled]="(processingClaim$ | async)"
|
[disabled]="(processing$ | async)"
|
||||||
(click)="claim()">
|
(click)="claim()">
|
||||||
<span *ngIf="(processingClaim$ | async)"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
|
<span *ngIf="(processing$ | async)"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
|
||||||
<span *ngIf="!(processingClaim$ | async)"><i class="fas fa-hand-paper"></i> {{'submission.workflow.tasks.pool.claim' | translate}}</span>
|
<span *ngIf="!(processing$ | async)"><i class="fas fa-hand-paper"></i> {{'submission.workflow.tasks.pool.claim' | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing';
|
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
@@ -21,6 +21,12 @@ import { getMockRequestService } from '../../mocks/request.service.mock';
|
|||||||
import { RequestService } from '../../../core/data/request.service';
|
import { RequestService } from '../../../core/data/request.service';
|
||||||
import { getMockSearchService } from '../../mocks/search-service.mock';
|
import { getMockSearchService } from '../../mocks/search-service.mock';
|
||||||
import { SearchService } from '../../../core/shared/search/search.service';
|
import { SearchService } from '../../../core/shared/search/search.service';
|
||||||
|
import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service';
|
||||||
|
import { PoolTaskSearchResult } from '../../object-collection/shared/pool-task-search-result.model';
|
||||||
|
import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response';
|
||||||
|
|
||||||
|
let mockDataService: PoolTaskDataService;
|
||||||
|
let mockClaimedTaskDataService: ClaimedTaskDataService;
|
||||||
|
|
||||||
let component: PoolTaskActionsComponent;
|
let component: PoolTaskActionsComponent;
|
||||||
let fixture: ComponentFixture<PoolTaskActionsComponent>;
|
let fixture: ComponentFixture<PoolTaskActionsComponent>;
|
||||||
@@ -29,13 +35,9 @@ let mockObject: PoolTask;
|
|||||||
let notificationsServiceStub: NotificationsServiceStub;
|
let notificationsServiceStub: NotificationsServiceStub;
|
||||||
let router: RouterStub;
|
let router: RouterStub;
|
||||||
|
|
||||||
const mockDataService = jasmine.createSpyObj('PoolTaskDataService', {
|
|
||||||
claimTask: jasmine.createSpy('claimTask')
|
|
||||||
});
|
|
||||||
|
|
||||||
const searchService = getMockSearchService();
|
const searchService = getMockSearchService();
|
||||||
|
|
||||||
const requestServce = getMockRequestService();
|
const requestService = getMockRequestService();
|
||||||
|
|
||||||
const item = Object.assign(new Item(), {
|
const item = Object.assign(new Item(), {
|
||||||
bundles: observableOf({}),
|
bundles: observableOf({}),
|
||||||
@@ -73,6 +75,8 @@ mockObject = Object.assign(new PoolTask(), { workflowitem: observableOf(rdWorkfl
|
|||||||
|
|
||||||
describe('PoolTaskActionsComponent', () => {
|
describe('PoolTaskActionsComponent', () => {
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
|
mockDataService = new PoolTaskDataService(null, null, null, null, null, null, null, null);
|
||||||
|
mockClaimedTaskDataService = new ClaimedTaskDataService(null, null, null, null, null, null, null, null);
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
TranslateModule.forRoot({
|
TranslateModule.forRoot({
|
||||||
@@ -88,8 +92,9 @@ describe('PoolTaskActionsComponent', () => {
|
|||||||
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||||
{ provide: Router, useValue: new RouterStub() },
|
{ provide: Router, useValue: new RouterStub() },
|
||||||
{ provide: PoolTaskDataService, useValue: mockDataService },
|
{ provide: PoolTaskDataService, useValue: mockDataService },
|
||||||
|
{ provide: ClaimedTaskDataService, useValue: mockClaimedTaskDataService },
|
||||||
{ provide: SearchService, useValue: searchService },
|
{ provide: SearchService, useValue: searchService },
|
||||||
{ provide: RequestService, useValue: requestServce }
|
{ provide: RequestService, useValue: requestService }
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).overrideComponent(PoolTaskActionsComponent, {
|
}).overrideComponent(PoolTaskActionsComponent, {
|
||||||
@@ -128,63 +133,35 @@ describe('PoolTaskActionsComponent', () => {
|
|||||||
expect(btn).toBeDefined();
|
expect(btn).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call claimTask method on claim', fakeAsync(() => {
|
it('should call claim task with href of getPoolTaskEndpointById', ((done) => {
|
||||||
spyOn(component, 'reload');
|
|
||||||
mockDataService.claimTask.and.returnValue(observableOf({ hasSucceeded: true }));
|
|
||||||
|
|
||||||
component.claim();
|
const poolTaskHref = 'poolTaskHref';
|
||||||
fixture.detectChanges();
|
const remoteClaimTaskResponse: any = new ProcessTaskResponse(true, null, null);
|
||||||
|
const remoteReloadedObjectResponse: any = createSuccessfulRemoteDataObject(new PoolTask());
|
||||||
|
|
||||||
fixture.whenStable().then(() => {
|
spyOn(mockDataService, 'getPoolTaskEndpointById').and.returnValue(observableOf(poolTaskHref));
|
||||||
expect(mockDataService.claimTask).toHaveBeenCalledWith(mockObject.id);
|
spyOn(mockClaimedTaskDataService, 'claimTask').and.returnValue(observableOf(remoteClaimTaskResponse));
|
||||||
});
|
spyOn(mockClaimedTaskDataService, 'findByItem').and.returnValue(observableOf(remoteReloadedObjectResponse));
|
||||||
|
|
||||||
}));
|
(component as any).objectDataService = mockDataService;
|
||||||
|
|
||||||
it('should display a success notification on claim success', waitForAsync(() => {
|
spyOn(component, 'handleReloadableActionResponse').and.callThrough();
|
||||||
spyOn(component, 'reload');
|
|
||||||
mockDataService.claimTask.and.returnValue(observableOf({ hasSucceeded: true }));
|
|
||||||
|
|
||||||
component.claim();
|
component.startActionExecution().subscribe( (result) => {
|
||||||
fixture.detectChanges();
|
|
||||||
|
expect(mockDataService.getPoolTaskEndpointById).toHaveBeenCalledWith(mockObject.id);
|
||||||
|
expect(mockClaimedTaskDataService.claimTask).toHaveBeenCalledWith(mockObject.id, poolTaskHref);
|
||||||
|
expect(mockClaimedTaskDataService.findByItem).toHaveBeenCalledWith(component.itemUuid);
|
||||||
|
|
||||||
|
expect(result instanceof PoolTaskSearchResult).toBeTrue();
|
||||||
|
|
||||||
|
expect(component.handleReloadableActionResponse).toHaveBeenCalledWith(true, result);
|
||||||
|
|
||||||
fixture.whenStable().then(() => {
|
|
||||||
expect(notificationsServiceStub.success).toHaveBeenCalled();
|
expect(notificationsServiceStub.success).toHaveBeenCalled();
|
||||||
|
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
it('should reload page on claim success', waitForAsync(() => {
|
|
||||||
spyOn(router, 'navigateByUrl');
|
|
||||||
router.url = 'test.url/test';
|
|
||||||
mockDataService.claimTask.and.returnValue(observableOf({ hasSucceeded: true }));
|
|
||||||
|
|
||||||
component.claim();
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
fixture.whenStable().then(() => {
|
|
||||||
expect(router.navigateByUrl).toHaveBeenCalledWith('test.url/test');
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should display an error notification on claim failure', waitForAsync(() => {
|
|
||||||
mockDataService.claimTask.and.returnValue(observableOf({ hasSucceeded: false }));
|
|
||||||
|
|
||||||
component.claim();
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
fixture.whenStable().then(() => {
|
|
||||||
expect(notificationsServiceStub.error).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should clear the object cache by href', waitForAsync(() => {
|
|
||||||
component.reload();
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
fixture.whenStable().then(() => {
|
|
||||||
expect(searchService.getEndpoint).toHaveBeenCalled();
|
|
||||||
expect(requestServce.removeByHrefSubstring).toHaveBeenCalledWith('discover/search/objects');
|
|
||||||
});
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -1,20 +1,24 @@
|
|||||||
import { Component, Injector, Input } from '@angular/core';
|
import { Component, Injector, Input, OnDestroy } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { filter, map } from 'rxjs/operators';
|
import {filter, map, switchMap, take} from 'rxjs/operators';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { WorkflowItem } from '../../../core/submission/models/workflowitem.model';
|
import { WorkflowItem } from '../../../core/submission/models/workflowitem.model';
|
||||||
import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response';
|
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { PoolTask } from '../../../core/tasks/models/pool-task-object.model';
|
import { PoolTask } from '../../../core/tasks/models/pool-task-object.model';
|
||||||
import { PoolTaskDataService } from '../../../core/tasks/pool-task-data.service';
|
import { PoolTaskDataService } from '../../../core/tasks/pool-task-data.service';
|
||||||
import { isNotUndefined } from '../../empty.util';
|
import { isNotUndefined } from '../../empty.util';
|
||||||
import { MyDSpaceActionsComponent } from '../mydspace-actions';
|
|
||||||
import { NotificationsService } from '../../notifications/notifications.service';
|
import { NotificationsService } from '../../notifications/notifications.service';
|
||||||
import { RequestService } from '../../../core/data/request.service';
|
import { RequestService } from '../../../core/data/request.service';
|
||||||
import { SearchService } from '../../../core/shared/search/search.service';
|
import { SearchService } from '../../../core/shared/search/search.service';
|
||||||
|
import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service';
|
||||||
|
import { getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators';
|
||||||
|
import { Item } from '../../../core/shared/item.model';
|
||||||
|
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||||
|
import { MyDSpaceReloadableActionsComponent } from '../mydspace-reloadable-actions';
|
||||||
|
import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component represents mydspace actions related to PoolTask object.
|
* This component represents mydspace actions related to PoolTask object.
|
||||||
@@ -24,24 +28,25 @@ import { SearchService } from '../../../core/shared/search/search.service';
|
|||||||
styleUrls: ['./pool-task-actions.component.scss'],
|
styleUrls: ['./pool-task-actions.component.scss'],
|
||||||
templateUrl: './pool-task-actions.component.html',
|
templateUrl: './pool-task-actions.component.html',
|
||||||
})
|
})
|
||||||
export class PoolTaskActionsComponent extends MyDSpaceActionsComponent<PoolTask, PoolTaskDataService> {
|
export class PoolTaskActionsComponent extends MyDSpaceReloadableActionsComponent<PoolTask, PoolTaskDataService> implements OnDestroy {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The PoolTask object
|
* The PoolTask object
|
||||||
*/
|
*/
|
||||||
@Input() object: PoolTask;
|
@Input() object: PoolTask;
|
||||||
|
|
||||||
/**
|
|
||||||
* A boolean representing if a claim operation is pending
|
|
||||||
* @type {BehaviorSubject<boolean>}
|
|
||||||
*/
|
|
||||||
public processingClaim$ = new BehaviorSubject<boolean>(false);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The workflowitem object that belonging to the PoolTask object
|
* The workflowitem object that belonging to the PoolTask object
|
||||||
*/
|
*/
|
||||||
public workflowitem$: Observable<WorkflowItem>;
|
public workflowitem$: Observable<WorkflowItem>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Anchor used to reload the pool task.
|
||||||
|
*/
|
||||||
|
public itemUuid: string;
|
||||||
|
|
||||||
|
subs = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize instance variables
|
* Initialize instance variables
|
||||||
*
|
*
|
||||||
@@ -55,6 +60,7 @@ export class PoolTaskActionsComponent extends MyDSpaceActionsComponent<PoolTask,
|
|||||||
constructor(protected injector: Injector,
|
constructor(protected injector: Injector,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected notificationsService: NotificationsService,
|
protected notificationsService: NotificationsService,
|
||||||
|
protected claimedTaskService: ClaimedTaskDataService,
|
||||||
protected translate: TranslateService,
|
protected translate: TranslateService,
|
||||||
protected searchService: SearchService,
|
protected searchService: SearchService,
|
||||||
protected requestService: RequestService) {
|
protected requestService: RequestService) {
|
||||||
@@ -62,10 +68,10 @@ export class PoolTaskActionsComponent extends MyDSpaceActionsComponent<PoolTask,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize objects
|
* Claim the task.
|
||||||
*/
|
*/
|
||||||
ngOnInit() {
|
claim() {
|
||||||
this.initObjects(this.object);
|
this.subs.push(this.startActionExecution().pipe(take(1)).subscribe());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,18 +83,36 @@ export class PoolTaskActionsComponent extends MyDSpaceActionsComponent<PoolTask,
|
|||||||
this.object = object;
|
this.object = object;
|
||||||
this.workflowitem$ = (this.object.workflowitem as Observable<RemoteData<WorkflowItem>>).pipe(
|
this.workflowitem$ = (this.object.workflowitem as Observable<RemoteData<WorkflowItem>>).pipe(
|
||||||
filter((rd: RemoteData<WorkflowItem>) => ((!rd.isRequestPending) && isNotUndefined(rd.payload))),
|
filter((rd: RemoteData<WorkflowItem>) => ((!rd.isRequestPending) && isNotUndefined(rd.payload))),
|
||||||
map((rd: RemoteData<WorkflowItem>) => rd.payload));
|
map((rd: RemoteData<WorkflowItem>) => rd.payload),
|
||||||
|
take(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
actionExecution(): Observable<ProcessTaskResponse> {
|
||||||
|
return this.objectDataService.getPoolTaskEndpointById(this.object.id)
|
||||||
|
.pipe(switchMap((poolTaskHref) => {
|
||||||
|
return this.claimedTaskService.claimTask(this.object.id, poolTaskHref);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadObjectExecution(): Observable<RemoteData<DSpaceObject> | DSpaceObject> {
|
||||||
|
return this.claimedTaskService.findByItem(this.itemUuid).pipe(take(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Claim the task.
|
* Retrieve the itemUuid.
|
||||||
*/
|
*/
|
||||||
claim() {
|
initReloadAnchor() {
|
||||||
this.processingClaim$.next(true);
|
(this.object as any).workflowitem.pipe(
|
||||||
this.objectDataService.claimTask(this.object.id)
|
getFirstSucceededRemoteDataPayload(),
|
||||||
.subscribe((res: ProcessTaskResponse) => {
|
switchMap((workflowItem: WorkflowItem) => workflowItem.item.pipe(getFirstSucceededRemoteDataPayload())
|
||||||
this.handleActionResponse(res.hasSucceeded);
|
))
|
||||||
this.processingClaim$.next(false);
|
.subscribe((item: Item) => {
|
||||||
});
|
this.itemUuid = item.uuid;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.subs.forEach((sub) => sub.unsubscribe());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -18,6 +18,7 @@
|
|||||||
[importable]="importable"
|
[importable]="importable"
|
||||||
[importConfig]="importConfig"
|
[importConfig]="importConfig"
|
||||||
(importObject)="importObject.emit($event)"
|
(importObject)="importObject.emit($event)"
|
||||||
|
(contentChange)="contentChange.emit()"
|
||||||
*ngIf="(currentMode$ | async) === viewModeEnum.ListElement">
|
*ngIf="(currentMode$ | async) === viewModeEnum.ListElement">
|
||||||
</ds-object-list>
|
</ds-object-list>
|
||||||
|
|
||||||
|
@@ -53,6 +53,11 @@ export class ObjectCollectionComponent implements OnInit {
|
|||||||
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||||
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit when one of the collection's object has changed.
|
||||||
|
*/
|
||||||
|
@Output() contentChange = new EventEmitter<any>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not to add an import button to the object elements
|
* Whether or not to add an import button to the object elements
|
||||||
*/
|
*/
|
||||||
|
@@ -0,0 +1,8 @@
|
|||||||
|
import { ClaimedTask } from '../../../core/tasks/models/claimed-task-object.model';
|
||||||
|
import { SearchResult } from '../../search/search-result.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a search result object of an Approved ClaimedTask object
|
||||||
|
*/
|
||||||
|
export class ClaimedApprovedTaskSearchResult extends SearchResult<ClaimedTask> {
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
import { ClaimedTask } from '../../../core/tasks/models/claimed-task-object.model';
|
||||||
|
import { SearchResult } from '../../search/search-result.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a search result object of a Declined ClaimedTask object
|
||||||
|
*/
|
||||||
|
export class ClaimedDeclinedTaskSearchResult extends SearchResult<ClaimedTask> {
|
||||||
|
}
|
@@ -0,0 +1,3 @@
|
|||||||
|
:host {
|
||||||
|
width: 100%;
|
||||||
|
}
|
@@ -1,14 +1,12 @@
|
|||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { waitForAsync, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||||
import { ChangeDetectionStrategy, ComponentFactoryResolver, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, ComponentFactoryResolver, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { ListableObjectComponentLoaderComponent } from './listable-object-component-loader.component';
|
import { ListableObjectComponentLoaderComponent } from './listable-object-component-loader.component';
|
||||||
import { ListableObject } from '../listable-object.model';
|
import { ListableObject } from '../listable-object.model';
|
||||||
import { GenericConstructor } from '../../../../core/shared/generic-constructor';
|
import { GenericConstructor } from '../../../../core/shared/generic-constructor';
|
||||||
import { Context } from '../../../../core/shared/context.model';
|
import { Context } from '../../../../core/shared/context.model';
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
import * as listableObjectDecorators from './listable-object.decorator';
|
|
||||||
import { ItemListElementComponent } from '../../../object-list/item-list-element/item-types/item/item-list-element.component';
|
import { ItemListElementComponent } from '../../../object-list/item-list-element/item-types/item/item-list-element.component';
|
||||||
import { ListableObjectDirective } from './listable-object.directive';
|
import { ListableObjectDirective } from './listable-object.directive';
|
||||||
import { spyOnExported } from '../../../testing/utils.test';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
@@ -23,7 +21,7 @@ class TestType extends ListableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
xdescribe('ListableObjectComponentLoaderComponent', () => {
|
describe('ListableObjectComponentLoaderComponent', () => {
|
||||||
let comp: ListableObjectComponentLoaderComponent;
|
let comp: ListableObjectComponentLoaderComponent;
|
||||||
let fixture: ComponentFixture<ListableObjectComponentLoaderComponent>;
|
let fixture: ComponentFixture<ListableObjectComponentLoaderComponent>;
|
||||||
|
|
||||||
@@ -32,7 +30,7 @@ xdescribe('ListableObjectComponentLoaderComponent', () => {
|
|||||||
imports: [TranslateModule.forRoot()],
|
imports: [TranslateModule.forRoot()],
|
||||||
declarations: [ListableObjectComponentLoaderComponent, ItemListElementComponent, ListableObjectDirective],
|
declarations: [ListableObjectComponentLoaderComponent, ItemListElementComponent, ListableObjectDirective],
|
||||||
schemas: [NO_ERRORS_SCHEMA],
|
schemas: [NO_ERRORS_SCHEMA],
|
||||||
providers: [ComponentFactoryResolver]
|
providers: []
|
||||||
}).overrideComponent(ListableObjectComponentLoaderComponent, {
|
}).overrideComponent(ListableObjectComponentLoaderComponent, {
|
||||||
set: {
|
set: {
|
||||||
changeDetection: ChangeDetectionStrategy.Default,
|
changeDetection: ChangeDetectionStrategy.Default,
|
||||||
@@ -48,14 +46,14 @@ xdescribe('ListableObjectComponentLoaderComponent', () => {
|
|||||||
comp.object = new TestType();
|
comp.object = new TestType();
|
||||||
comp.viewMode = testViewMode;
|
comp.viewMode = testViewMode;
|
||||||
comp.context = testContext;
|
comp.context = testContext;
|
||||||
spyOnExported(listableObjectDecorators, 'getListableObjectComponent').and.returnValue(ItemListElementComponent);
|
spyOn(comp, 'getComponent').and.returnValue(ItemListElementComponent as any);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('When the component is rendered', () => {
|
describe('When the component is rendered', () => {
|
||||||
it('should call the getListableObjectComponent function with the right types, view mode and context', () => {
|
it('should call the getListableObjectComponent function with the right types, view mode and context', () => {
|
||||||
expect(listableObjectDecorators.getListableObjectComponent).toHaveBeenCalledWith([testType], testViewMode, testContext);
|
expect(comp.getComponent).toHaveBeenCalledWith([testType], testViewMode, testContext);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -117,4 +115,20 @@ xdescribe('ListableObjectComponentLoaderComponent', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('When a reloadedObject is emitted', () => {
|
||||||
|
|
||||||
|
it('should re-instantiate the listable component ', fakeAsync(() => {
|
||||||
|
|
||||||
|
spyOn((comp as any), 'instantiateComponent').and.returnValue(null);
|
||||||
|
|
||||||
|
const listableComponent = fixture.debugElement.query(By.css('ds-item-list-element')).componentInstance;
|
||||||
|
const reloadedObject: any = 'object';
|
||||||
|
(listableComponent as any).reloadedObject.emit(reloadedObject);
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect((comp as any).instantiateComponent).toHaveBeenCalledWith(reloadedObject);
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -1,4 +1,14 @@
|
|||||||
import { Component, ComponentFactoryResolver, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
|
import {
|
||||||
|
Component,
|
||||||
|
ComponentFactoryResolver,
|
||||||
|
ElementRef,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
Output,
|
||||||
|
ViewChild,
|
||||||
|
EventEmitter
|
||||||
|
} from '@angular/core';
|
||||||
import { ListableObject } from '../listable-object.model';
|
import { ListableObject } from '../listable-object.model';
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
import { Context } from '../../../../core/shared/context.model';
|
import { Context } from '../../../../core/shared/context.model';
|
||||||
@@ -7,16 +17,19 @@ import { GenericConstructor } from '../../../../core/shared/generic-constructor'
|
|||||||
import { ListableObjectDirective } from './listable-object.directive';
|
import { ListableObjectDirective } from './listable-object.directive';
|
||||||
import { CollectionElementLinkType } from '../../collection-element-link.type';
|
import { CollectionElementLinkType } from '../../collection-element-link.type';
|
||||||
import { hasValue } from '../../../empty.util';
|
import { hasValue } from '../../../empty.util';
|
||||||
|
import { Subscription } from 'rxjs/internal/Subscription';
|
||||||
|
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
||||||
|
import { take } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-listable-object-component-loader',
|
selector: 'ds-listable-object-component-loader',
|
||||||
// styleUrls: ['./listable-object-component-loader.component.scss'],
|
styleUrls: ['./listable-object-component-loader.component.scss'],
|
||||||
templateUrl: './listable-object-component-loader.component.html'
|
templateUrl: './listable-object-component-loader.component.html'
|
||||||
})
|
})
|
||||||
/**
|
/**
|
||||||
* Component for determining what component to use depending on the item's relationship type (relationship.type)
|
* Component for determining what component to use depending on the item's relationship type (relationship.type)
|
||||||
*/
|
*/
|
||||||
export class ListableObjectComponentLoaderComponent implements OnInit {
|
export class ListableObjectComponentLoaderComponent implements OnInit, OnDestroy {
|
||||||
/**
|
/**
|
||||||
* The item or metadata to determine the component for
|
* The item or metadata to determine the component for
|
||||||
*/
|
*/
|
||||||
@@ -73,6 +86,11 @@ export class ListableObjectComponentLoaderComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
@ViewChild('badges', { static: true }) badges: ElementRef;
|
@ViewChild('badges', { static: true }) badges: ElementRef;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit when the listable object has been reloaded.
|
||||||
|
*/
|
||||||
|
@Output() contentChange = new EventEmitter<ListableObject>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the "Private" badge should be displayed for this listable object
|
* Whether or not the "Private" badge should be displayed for this listable object
|
||||||
*/
|
*/
|
||||||
@@ -83,6 +101,12 @@ export class ListableObjectComponentLoaderComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
withdrawnBadge = false;
|
withdrawnBadge = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
|
protected subs: Subscription[] = [];
|
||||||
|
|
||||||
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
|
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,9 +114,22 @@ export class ListableObjectComponentLoaderComponent implements OnInit {
|
|||||||
* Setup the dynamic child component
|
* Setup the dynamic child component
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.instantiateComponent(this.object);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.subs
|
||||||
|
.filter((subscription) => hasValue(subscription))
|
||||||
|
.forEach((subscription) => subscription.unsubscribe());
|
||||||
|
}
|
||||||
|
|
||||||
|
private instantiateComponent(object) {
|
||||||
|
|
||||||
this.initBadges();
|
this.initBadges();
|
||||||
|
|
||||||
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent());
|
const component = this.getComponent(object.getRenderTypes(), this.viewMode, this.context);
|
||||||
|
|
||||||
|
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
|
||||||
|
|
||||||
const viewContainerRef = this.listableObjectDirective.viewContainerRef;
|
const viewContainerRef = this.listableObjectDirective.viewContainerRef;
|
||||||
viewContainerRef.clear();
|
viewContainerRef.clear();
|
||||||
@@ -104,7 +141,7 @@ export class ListableObjectComponentLoaderComponent implements OnInit {
|
|||||||
[
|
[
|
||||||
[this.badges.nativeElement],
|
[this.badges.nativeElement],
|
||||||
]);
|
]);
|
||||||
(componentRef.instance as any).object = this.object;
|
(componentRef.instance as any).object = object;
|
||||||
(componentRef.instance as any).index = this.index;
|
(componentRef.instance as any).index = this.index;
|
||||||
(componentRef.instance as any).linkType = this.linkType;
|
(componentRef.instance as any).linkType = this.linkType;
|
||||||
(componentRef.instance as any).listID = this.listID;
|
(componentRef.instance as any).listID = this.listID;
|
||||||
@@ -112,6 +149,17 @@ export class ListableObjectComponentLoaderComponent implements OnInit {
|
|||||||
(componentRef.instance as any).context = this.context;
|
(componentRef.instance as any).context = this.context;
|
||||||
(componentRef.instance as any).viewMode = this.viewMode;
|
(componentRef.instance as any).viewMode = this.viewMode;
|
||||||
(componentRef.instance as any).value = this.value;
|
(componentRef.instance as any).value = this.value;
|
||||||
|
|
||||||
|
if ((componentRef.instance as any).reloadedObject) {
|
||||||
|
(componentRef.instance as any).reloadedObject.pipe(take(1)).subscribe((reloadedObject: DSpaceObject) => {
|
||||||
|
if (reloadedObject) {
|
||||||
|
componentRef.destroy();
|
||||||
|
this.object = reloadedObject;
|
||||||
|
this.instantiateComponent(reloadedObject);
|
||||||
|
this.contentChange.emit(reloadedObject);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,7 +179,9 @@ export class ListableObjectComponentLoaderComponent implements OnInit {
|
|||||||
* Fetch the component depending on the item's relationship type, view mode and context
|
* Fetch the component depending on the item's relationship type, view mode and context
|
||||||
* @returns {GenericConstructor<Component>}
|
* @returns {GenericConstructor<Component>}
|
||||||
*/
|
*/
|
||||||
private getComponent(): GenericConstructor<Component> {
|
getComponent(renderTypes: (string | GenericConstructor<ListableObject>)[],
|
||||||
return getListableObjectComponent(this.object.getRenderTypes(), this.viewMode, this.context);
|
viewMode: ViewMode,
|
||||||
|
context: Context): GenericConstructor<Component> {
|
||||||
|
return getListableObjectComponent(renderTypes, viewMode, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,5 +3,7 @@ export enum MyDspaceItemStatusType {
|
|||||||
VALIDATION = 'mydspace.status.validation',
|
VALIDATION = 'mydspace.status.validation',
|
||||||
WAITING_CONTROLLER = 'mydspace.status.waiting-for-controller',
|
WAITING_CONTROLLER = 'mydspace.status.waiting-for-controller',
|
||||||
WORKSPACE = 'mydspace.status.workspace',
|
WORKSPACE = 'mydspace.status.workspace',
|
||||||
ARCHIVED = 'mydspace.status.archived'
|
ARCHIVED = 'mydspace.status.archived',
|
||||||
|
DECLINED = 'mydspace.status.declined',
|
||||||
|
APPROVED = 'mydspace.status.approved',
|
||||||
}
|
}
|
||||||
|
@@ -1,14 +1,16 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
import { ListableObject } from '../listable-object.model';
|
import { ListableObject } from '../listable-object.model';
|
||||||
import { CollectionElementLinkType } from '../../collection-element-link.type';
|
import { CollectionElementLinkType } from '../../collection-element-link.type';
|
||||||
import { Context } from '../../../../core/shared/context.model';
|
import { Context } from '../../../../core/shared/context.model';
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
|
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-abstract-object-element',
|
selector: 'ds-abstract-object-element',
|
||||||
template: ``,
|
template: ``,
|
||||||
})
|
})
|
||||||
export class AbstractListableElementComponent<T extends ListableObject> {
|
export class AbstractListableElementComponent<T extends ListableObject> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The object to render in this list element
|
* The object to render in this list element
|
||||||
*/
|
*/
|
||||||
@@ -49,6 +51,11 @@ export class AbstractListableElementComponent<T extends ListableObject> {
|
|||||||
*/
|
*/
|
||||||
@Input() viewMode: ViewMode;
|
@Input() viewMode: ViewMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit when the object has been reloaded.
|
||||||
|
*/
|
||||||
|
@Output() reloadedObject = new EventEmitter<DSpaceObject>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The available link types
|
* The available link types
|
||||||
*/
|
*/
|
||||||
|
@@ -6,5 +6,5 @@
|
|||||||
[status]="status">
|
[status]="status">
|
||||||
</ds-item-detail-preview>
|
</ds-item-detail-preview>
|
||||||
|
|
||||||
<ds-claimed-task-actions *ngIf="workflowitem" [object]="dso"></ds-claimed-task-actions>
|
<ds-claimed-task-actions *ngIf="workflowitem" [object]="dso" (processCompleted)="reloadedObject.emit($event.reloadedObject)"></ds-claimed-task-actions>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, TestBed, tick, waitForAsync, fakeAsync} from '@angular/core/testing';
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
@@ -14,6 +14,7 @@ import { ClaimedTaskSearchResult } from '../../../object-collection/shared/claim
|
|||||||
import { VarDirective } from '../../../utils/var.directive';
|
import { VarDirective } from '../../../utils/var.directive';
|
||||||
import { LinkService } from '../../../../core/cache/builders/link.service';
|
import { LinkService } from '../../../../core/cache/builders/link.service';
|
||||||
import { getMockLinkService } from '../../../mocks/link-service.mock';
|
import { getMockLinkService } from '../../../mocks/link-service.mock';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
let component: ClaimedTaskSearchResultDetailElementComponent;
|
let component: ClaimedTaskSearchResultDetailElementComponent;
|
||||||
let fixture: ComponentFixture<ClaimedTaskSearchResultDetailElementComponent>;
|
let fixture: ComponentFixture<ClaimedTaskSearchResultDetailElementComponent>;
|
||||||
@@ -98,4 +99,16 @@ describe('ClaimedTaskSearchResultDetailElementComponent', () => {
|
|||||||
it('should have properly status', () => {
|
it('should have properly status', () => {
|
||||||
expect(component.status).toEqual(MyDspaceItemStatusType.VALIDATION);
|
expect(component.status).toEqual(MyDspaceItemStatusType.VALIDATION);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should forward claimed-task-actions processComplete event to reloadObject event emitter', fakeAsync(() => {
|
||||||
|
spyOn(component.reloadedObject, 'emit').and.callThrough();
|
||||||
|
const actionPayload: any = { reloadedObject: {}};
|
||||||
|
|
||||||
|
const actionsComponent = fixture.debugElement.query(By.css('ds-claimed-task-actions'));
|
||||||
|
actionsComponent.triggerEventHandler('processCompleted', actionPayload);
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(component.reloadedObject.emit).toHaveBeenCalledWith(actionPayload.reloadedObject);
|
||||||
|
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
@@ -5,5 +5,5 @@
|
|||||||
[showSubmitter]="showSubmitter"
|
[showSubmitter]="showSubmitter"
|
||||||
[status]="status"></ds-item-detail-preview>
|
[status]="status"></ds-item-detail-preview>
|
||||||
|
|
||||||
<ds-pool-task-actions *ngIf="workflowitem" [object]="dso"></ds-pool-task-actions>
|
<ds-pool-task-actions *ngIf="workflowitem" [object]="dso" (processCompleted)="reloadedObject.emit($event.reloadedObject)"></ds-pool-task-actions>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { waitForAsync, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
@@ -14,6 +14,7 @@ import { PoolTaskSearchResult } from '../../../object-collection/shared/pool-tas
|
|||||||
import { VarDirective } from '../../../utils/var.directive';
|
import { VarDirective } from '../../../utils/var.directive';
|
||||||
import { LinkService } from '../../../../core/cache/builders/link.service';
|
import { LinkService } from '../../../../core/cache/builders/link.service';
|
||||||
import { getMockLinkService } from '../../../mocks/link-service.mock';
|
import { getMockLinkService } from '../../../mocks/link-service.mock';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
let component: PoolSearchResultDetailElementComponent;
|
let component: PoolSearchResultDetailElementComponent;
|
||||||
let fixture: ComponentFixture<PoolSearchResultDetailElementComponent>;
|
let fixture: ComponentFixture<PoolSearchResultDetailElementComponent>;
|
||||||
@@ -99,4 +100,15 @@ describe('PoolSearchResultDetailElementComponent', () => {
|
|||||||
it('should have properly status', () => {
|
it('should have properly status', () => {
|
||||||
expect(component.status).toEqual(MyDspaceItemStatusType.WAITING_CONTROLLER);
|
expect(component.status).toEqual(MyDspaceItemStatusType.WAITING_CONTROLLER);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should forward pool-task-actions processCompleted event to the reloadedObject event emitter', fakeAsync(() => {
|
||||||
|
spyOn(component.reloadedObject, 'emit').and.callThrough();
|
||||||
|
const actionPayload: any = { reloadedObject: {}};
|
||||||
|
const actionsComponents = fixture.debugElement.query(By.css('ds-pool-task-actions'));
|
||||||
|
actionsComponents.triggerEventHandler('processCompleted', actionPayload);
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(component.reloadedObject.emit).toHaveBeenCalledWith(actionPayload.reloadedObject);
|
||||||
|
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
@@ -0,0 +1,10 @@
|
|||||||
|
<ng-container *ngVar="(workflowitemRD$ | async)?.payload as workflowitem">
|
||||||
|
<div class="alert alert-success w-100" role="alert">
|
||||||
|
<h4 class="alert-heading">Approved</h4>
|
||||||
|
<ds-item-list-preview *ngIf="workflowitem"
|
||||||
|
[item]="(workflowitem?.item | async)?.payload"
|
||||||
|
[object]="object"
|
||||||
|
[status]="status"
|
||||||
|
[showSubmitter]="showSubmitter"></ds-item-list-preview>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
@@ -0,0 +1,101 @@
|
|||||||
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
|
||||||
|
import { Item } from '../../../../../core/shared/item.model';
|
||||||
|
import { createSuccessfulRemoteDataObject } from '../../../../remote-data.utils';
|
||||||
|
import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model';
|
||||||
|
import { ClaimedTask } from '../../../../../core/tasks/models/claimed-task-object.model';
|
||||||
|
import { getMockLinkService } from '../../../../mocks/link-service.mock';
|
||||||
|
import { VarDirective } from '../../../../utils/var.directive';
|
||||||
|
import { TruncatableService } from '../../../../truncatable/truncatable.service';
|
||||||
|
import { LinkService } from '../../../../../core/cache/builders/link.service';
|
||||||
|
import { MyDspaceItemStatusType } from '../../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
|
||||||
|
import { ClaimedApprovedTaskSearchResult } from '../../../../object-collection/shared/claimed-approved-task-search-result.model';
|
||||||
|
import { ClaimedApprovedSearchResultListElementComponent } from './claimed-approved-search-result-list-element.component';
|
||||||
|
|
||||||
|
let component: ClaimedApprovedSearchResultListElementComponent;
|
||||||
|
let fixture: ComponentFixture<ClaimedApprovedSearchResultListElementComponent>;
|
||||||
|
|
||||||
|
const mockResultObject: ClaimedApprovedTaskSearchResult = new ClaimedApprovedTaskSearchResult();
|
||||||
|
mockResultObject.hitHighlights = {};
|
||||||
|
|
||||||
|
const item = Object.assign(new Item(), {
|
||||||
|
bundles: observableOf({}),
|
||||||
|
metadata: {
|
||||||
|
'dc.title': [
|
||||||
|
{
|
||||||
|
language: 'en_US',
|
||||||
|
value: 'This is just another title'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'dc.type': [
|
||||||
|
{
|
||||||
|
language: null,
|
||||||
|
value: 'Article'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'dc.contributor.author': [
|
||||||
|
{
|
||||||
|
language: 'en_US',
|
||||||
|
value: 'Smith, Donald'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'dc.date.issued': [
|
||||||
|
{
|
||||||
|
language: null,
|
||||||
|
value: '2015-06-26'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const rdItem = createSuccessfulRemoteDataObject(item);
|
||||||
|
const workflowitem = Object.assign(new WorkflowItem(), { item: observableOf(rdItem) });
|
||||||
|
const rdWorkflowitem = createSuccessfulRemoteDataObject(workflowitem);
|
||||||
|
mockResultObject.indexableObject = Object.assign(new ClaimedTask(), { workflowitem: observableOf(rdWorkflowitem) });
|
||||||
|
const linkService = getMockLinkService();
|
||||||
|
|
||||||
|
describe('ClaimedApprovedSearchResultListElementComponent', () => {
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [NoopAnimationsModule],
|
||||||
|
declarations: [ClaimedApprovedSearchResultListElementComponent, VarDirective],
|
||||||
|
providers: [
|
||||||
|
{ provide: TruncatableService, useValue: {} },
|
||||||
|
{ provide: LinkService, useValue: linkService }
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
}).overrideComponent(ClaimedApprovedSearchResultListElementComponent, {
|
||||||
|
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
fixture = TestBed.createComponent(ClaimedApprovedSearchResultListElementComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
component.dso = mockResultObject.indexableObject;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should init workflowitem properly', (done) => {
|
||||||
|
component.workflowitemRD$.subscribe((workflowitemRD) => {
|
||||||
|
expect(linkService.resolveLinks).toHaveBeenCalledWith(
|
||||||
|
component.dso,
|
||||||
|
jasmine.objectContaining({ name: 'workflowitem' }),
|
||||||
|
jasmine.objectContaining({ name: 'action' })
|
||||||
|
);
|
||||||
|
expect(workflowitemRD.payload).toEqual(workflowitem);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have properly status', () => {
|
||||||
|
expect(component.status).toEqual(MyDspaceItemStatusType.APPROVED);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,68 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { ViewMode } from '../../../../../core/shared/view-mode.model';
|
||||||
|
import { ClaimedApprovedTaskSearchResult } from '../../../../object-collection/shared/claimed-approved-task-search-result.model';
|
||||||
|
import { listableObjectComponent } from '../../../../object-collection/shared/listable-object/listable-object.decorator';
|
||||||
|
import { LinkService } from '../../../../../core/cache/builders/link.service';
|
||||||
|
import { TruncatableService } from '../../../../truncatable/truncatable.service';
|
||||||
|
import { MyDspaceItemStatusType } from '../../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { RemoteData } from '../../../../../core/data/remote-data';
|
||||||
|
import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model';
|
||||||
|
import { followLink } from '../../../../utils/follow-link-config.model';
|
||||||
|
import { SearchResultListElementComponent } from '../../../search-result-list-element/search-result-list-element.component';
|
||||||
|
import { ClaimedTaskSearchResult} from '../../../../object-collection/shared/claimed-task-search-result.model';
|
||||||
|
import { ClaimedTask } from '../../../../../core/tasks/models/claimed-task-object.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component renders claimed task approved object for the search result in the list view.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-claimed-approved-search-result-list-element',
|
||||||
|
styleUrls: ['../../../search-result-list-element/search-result-list-element.component.scss'],
|
||||||
|
templateUrl: './claimed-approved-search-result-list-element.component.html'
|
||||||
|
})
|
||||||
|
@listableObjectComponent(ClaimedApprovedTaskSearchResult, ViewMode.ListElement)
|
||||||
|
export class ClaimedApprovedSearchResultListElementComponent extends SearchResultListElementComponent<ClaimedTaskSearchResult, ClaimedTask> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if to show submitter information
|
||||||
|
*/
|
||||||
|
public showSubmitter = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent item's status
|
||||||
|
*/
|
||||||
|
public status = MyDspaceItemStatusType.APPROVED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The workflowitem object that belonging to the result object
|
||||||
|
*/
|
||||||
|
public workflowitemRD$: Observable<RemoteData<WorkflowItem>>;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
protected linkService: LinkService,
|
||||||
|
protected truncatableService: TruncatableService
|
||||||
|
) {
|
||||||
|
super(truncatableService);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all instance variables
|
||||||
|
*/
|
||||||
|
ngOnInit() {
|
||||||
|
super.ngOnInit();
|
||||||
|
this.linkService.resolveLinks(this.dso,
|
||||||
|
followLink('workflowitem',
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
followLink('item'),
|
||||||
|
followLink('submitter')
|
||||||
|
),
|
||||||
|
followLink('action')
|
||||||
|
);
|
||||||
|
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,101 @@
|
|||||||
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
|
||||||
|
import { ClaimedDeclinedSearchResultListElementComponent } from './claimed-declined-search-result-list-element.component';
|
||||||
|
import { ClaimedDeclinedTaskSearchResult } from '../../../../object-collection/shared/claimed-declined-task-search-result.model';
|
||||||
|
import { Item } from '../../../../../core/shared/item.model';
|
||||||
|
import { createSuccessfulRemoteDataObject } from '../../../../remote-data.utils';
|
||||||
|
import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model';
|
||||||
|
import { ClaimedTask } from '../../../../../core/tasks/models/claimed-task-object.model';
|
||||||
|
import { getMockLinkService } from '../../../../mocks/link-service.mock';
|
||||||
|
import { VarDirective } from '../../../../utils/var.directive';
|
||||||
|
import { TruncatableService } from '../../../../truncatable/truncatable.service';
|
||||||
|
import { LinkService } from '../../../../../core/cache/builders/link.service';
|
||||||
|
import { MyDspaceItemStatusType } from '../../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
|
||||||
|
|
||||||
|
let component: ClaimedDeclinedSearchResultListElementComponent;
|
||||||
|
let fixture: ComponentFixture<ClaimedDeclinedSearchResultListElementComponent>;
|
||||||
|
|
||||||
|
const mockResultObject: ClaimedDeclinedTaskSearchResult = new ClaimedDeclinedTaskSearchResult();
|
||||||
|
mockResultObject.hitHighlights = {};
|
||||||
|
|
||||||
|
const item = Object.assign(new Item(), {
|
||||||
|
bundles: observableOf({}),
|
||||||
|
metadata: {
|
||||||
|
'dc.title': [
|
||||||
|
{
|
||||||
|
language: 'en_US',
|
||||||
|
value: 'This is just another title'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'dc.type': [
|
||||||
|
{
|
||||||
|
language: null,
|
||||||
|
value: 'Article'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'dc.contributor.author': [
|
||||||
|
{
|
||||||
|
language: 'en_US',
|
||||||
|
value: 'Smith, Donald'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'dc.date.issued': [
|
||||||
|
{
|
||||||
|
language: null,
|
||||||
|
value: '2015-06-26'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const rdItem = createSuccessfulRemoteDataObject(item);
|
||||||
|
const workflowitem = Object.assign(new WorkflowItem(), { item: observableOf(rdItem) });
|
||||||
|
const rdWorkflowitem = createSuccessfulRemoteDataObject(workflowitem);
|
||||||
|
mockResultObject.indexableObject = Object.assign(new ClaimedTask(), { workflowitem: observableOf(rdWorkflowitem) });
|
||||||
|
const linkService = getMockLinkService();
|
||||||
|
|
||||||
|
describe('ClaimedDeclinedSearchResultListElementComponent', () => {
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [NoopAnimationsModule],
|
||||||
|
declarations: [ClaimedDeclinedSearchResultListElementComponent, VarDirective],
|
||||||
|
providers: [
|
||||||
|
{ provide: TruncatableService, useValue: {} },
|
||||||
|
{ provide: LinkService, useValue: linkService }
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
}).overrideComponent(ClaimedDeclinedSearchResultListElementComponent, {
|
||||||
|
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
fixture = TestBed.createComponent(ClaimedDeclinedSearchResultListElementComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
component.dso = mockResultObject.indexableObject;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should init workflowitem properly', (done) => {
|
||||||
|
component.workflowitemRD$.subscribe((workflowitemRD) => {
|
||||||
|
expect(linkService.resolveLinks).toHaveBeenCalledWith(
|
||||||
|
component.dso,
|
||||||
|
jasmine.objectContaining({ name: 'workflowitem' }),
|
||||||
|
jasmine.objectContaining({ name: 'action' })
|
||||||
|
);
|
||||||
|
expect(workflowitemRD.payload).toEqual(workflowitem);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have properly status', () => {
|
||||||
|
expect(component.status).toEqual(MyDspaceItemStatusType.DECLINED);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,10 @@
|
|||||||
|
<ng-container *ngVar="(workflowitemRD$ | async)?.payload as workflowitem">
|
||||||
|
<div class="alert alert-secondary w-100" role="alert">
|
||||||
|
<h4 class="alert-heading">Declined</h4>
|
||||||
|
<ds-item-list-preview *ngIf="workflowitem"
|
||||||
|
[item]="(workflowitem?.item | async)?.payload"
|
||||||
|
[object]="object"
|
||||||
|
[status]="status"
|
||||||
|
[showSubmitter]="showSubmitter"></ds-item-list-preview>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
@@ -0,0 +1,68 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
import { listableObjectComponent } from '../../../../object-collection/shared/listable-object/listable-object.decorator';
|
||||||
|
import { ClaimedDeclinedTaskSearchResult } from '../../../../object-collection/shared/claimed-declined-task-search-result.model';
|
||||||
|
import { ViewMode } from '../../../../../core/shared/view-mode.model';
|
||||||
|
import { LinkService } from '../../../../../core/cache/builders/link.service';
|
||||||
|
import { TruncatableService } from '../../../../truncatable/truncatable.service';
|
||||||
|
import { MyDspaceItemStatusType } from '../../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { RemoteData } from '../../../../../core/data/remote-data';
|
||||||
|
import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model';
|
||||||
|
import { followLink } from '../../../../utils/follow-link-config.model';
|
||||||
|
import { SearchResultListElementComponent } from '../../../search-result-list-element/search-result-list-element.component';
|
||||||
|
import { ClaimedTaskSearchResult } from '../../../../object-collection/shared/claimed-task-search-result.model';
|
||||||
|
import { ClaimedTask } from '../../../../../core/tasks/models/claimed-task-object.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component renders claimed task declined object for the search result in the list view.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-claimed-declined-search-result-list-element',
|
||||||
|
styleUrls: ['../../../search-result-list-element/search-result-list-element.component.scss'],
|
||||||
|
templateUrl: './claimed-declined-search-result-list-element.component.html'
|
||||||
|
})
|
||||||
|
@listableObjectComponent(ClaimedDeclinedTaskSearchResult, ViewMode.ListElement)
|
||||||
|
export class ClaimedDeclinedSearchResultListElementComponent extends SearchResultListElementComponent<ClaimedTaskSearchResult, ClaimedTask> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if to show submitter information
|
||||||
|
*/
|
||||||
|
public showSubmitter = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent item's status
|
||||||
|
*/
|
||||||
|
public status = MyDspaceItemStatusType.DECLINED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The workflowitem object that belonging to the result object
|
||||||
|
*/
|
||||||
|
public workflowitemRD$: Observable<RemoteData<WorkflowItem>>;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
protected linkService: LinkService,
|
||||||
|
protected truncatableService: TruncatableService
|
||||||
|
) {
|
||||||
|
super(truncatableService);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all instance variables
|
||||||
|
*/
|
||||||
|
ngOnInit() {
|
||||||
|
super.ngOnInit();
|
||||||
|
this.linkService.resolveLinks(this.dso,
|
||||||
|
followLink('workflowitem',
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
followLink('item'),
|
||||||
|
followLink('submitter')
|
||||||
|
),
|
||||||
|
followLink('action'));
|
||||||
|
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -4,7 +4,6 @@
|
|||||||
[object]="object"
|
[object]="object"
|
||||||
[showSubmitter]="showSubmitter"
|
[showSubmitter]="showSubmitter"
|
||||||
[status]="status"></ds-item-list-preview>
|
[status]="status"></ds-item-list-preview>
|
||||||
|
<ds-claimed-task-actions *ngIf="workflowitem" [object]="dso" (processCompleted)="reloadedObject.emit($event.reloadedObject)"></ds-claimed-task-actions>
|
||||||
<ds-claimed-task-actions *ngIf="workflowitem" [object]="dso"></ds-claimed-task-actions>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
@@ -15,12 +15,11 @@ import { TruncatableService } from '../../../truncatable/truncatable.service';
|
|||||||
import { VarDirective } from '../../../utils/var.directive';
|
import { VarDirective } from '../../../utils/var.directive';
|
||||||
import { LinkService } from '../../../../core/cache/builders/link.service';
|
import { LinkService } from '../../../../core/cache/builders/link.service';
|
||||||
import { getMockLinkService } from '../../../mocks/link-service.mock';
|
import { getMockLinkService } from '../../../mocks/link-service.mock';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
let component: ClaimedSearchResultListElementComponent;
|
let component: ClaimedSearchResultListElementComponent;
|
||||||
let fixture: ComponentFixture<ClaimedSearchResultListElementComponent>;
|
let fixture: ComponentFixture<ClaimedSearchResultListElementComponent>;
|
||||||
|
|
||||||
const compIndex = 1;
|
|
||||||
|
|
||||||
const mockResultObject: ClaimedTaskSearchResult = new ClaimedTaskSearchResult();
|
const mockResultObject: ClaimedTaskSearchResult = new ClaimedTaskSearchResult();
|
||||||
mockResultObject.hitHighlights = {};
|
mockResultObject.hitHighlights = {};
|
||||||
|
|
||||||
@@ -99,4 +98,16 @@ describe('ClaimedSearchResultListElementComponent', () => {
|
|||||||
it('should have properly status', () => {
|
it('should have properly status', () => {
|
||||||
expect(component.status).toEqual(MyDspaceItemStatusType.VALIDATION);
|
expect(component.status).toEqual(MyDspaceItemStatusType.VALIDATION);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should forward claimed-task-actions processComplete event to reloadObject event emitter', fakeAsync(() => {
|
||||||
|
spyOn(component.reloadedObject, 'emit').and.callThrough();
|
||||||
|
const actionPayload: any = { reloadedObject: {}};
|
||||||
|
|
||||||
|
const actionsComponent = fixture.debugElement.query(By.css('ds-claimed-task-actions'));
|
||||||
|
actionsComponent.triggerEventHandler('processCompleted', actionPayload);
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(component.reloadedObject.emit).toHaveBeenCalledWith(actionPayload.reloadedObject);
|
||||||
|
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
@@ -1,30 +1,23 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common';
|
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
|
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
|
||||||
import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model';
|
|
||||||
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
|
|
||||||
import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
|
|
||||||
import { listableObjectComponent } from '../../../object-collection/shared/listable-object/listable-object.decorator';
|
import { listableObjectComponent } from '../../../object-collection/shared/listable-object/listable-object.decorator';
|
||||||
import { ClaimedTaskSearchResult } from '../../../object-collection/shared/claimed-task-search-result.model';
|
import { ClaimedTaskSearchResult } from '../../../object-collection/shared/claimed-task-search-result.model';
|
||||||
import { SearchResultListElementComponent } from '../../search-result-list-element/search-result-list-element.component';
|
|
||||||
import { followLink } from '../../../utils/follow-link-config.model';
|
|
||||||
import { LinkService } from '../../../../core/cache/builders/link.service';
|
import { LinkService } from '../../../../core/cache/builders/link.service';
|
||||||
import { TruncatableService } from '../../../truncatable/truncatable.service';
|
import { TruncatableService } from '../../../truncatable/truncatable.service';
|
||||||
|
import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
|
import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model';
|
||||||
|
import { followLink } from '../../../utils/follow-link-config.model';
|
||||||
|
import { SearchResultListElementComponent } from '../../search-result-list-element/search-result-list-element.component';
|
||||||
|
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
|
||||||
|
|
||||||
/**
|
|
||||||
* This component renders claimed task object for the search result in the list view.
|
|
||||||
*/
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-claimed-search-result-list-element',
|
selector: 'ds-claimed-search-result-list-element',
|
||||||
styleUrls: ['../../search-result-list-element/search-result-list-element.component.scss'],
|
styleUrls: ['../../search-result-list-element/search-result-list-element.component.scss'],
|
||||||
templateUrl: './claimed-search-result-list-element.component.html',
|
templateUrl: './claimed-search-result-list-element.component.html'
|
||||||
providers: [Location, { provide: LocationStrategy, useClass: PathLocationStrategy }]
|
|
||||||
})
|
})
|
||||||
|
|
||||||
@listableObjectComponent(ClaimedTaskSearchResult, ViewMode.ListElement)
|
@listableObjectComponent(ClaimedTaskSearchResult, ViewMode.ListElement)
|
||||||
export class ClaimedSearchResultListElementComponent extends SearchResultListElementComponent<ClaimedTaskSearchResult, ClaimedTask> {
|
export class ClaimedSearchResultListElementComponent extends SearchResultListElementComponent<ClaimedTaskSearchResult, ClaimedTask> {
|
||||||
|
|
||||||
@@ -43,7 +36,7 @@ export class ClaimedSearchResultListElementComponent extends SearchResultListEle
|
|||||||
*/
|
*/
|
||||||
public workflowitemRD$: Observable<RemoteData<WorkflowItem>>;
|
public workflowitemRD$: Observable<RemoteData<WorkflowItem>>;
|
||||||
|
|
||||||
constructor(
|
public constructor(
|
||||||
protected linkService: LinkService,
|
protected linkService: LinkService,
|
||||||
protected truncatableService: TruncatableService
|
protected truncatableService: TruncatableService
|
||||||
) {
|
) {
|
||||||
@@ -60,4 +53,5 @@ export class ClaimedSearchResultListElementComponent extends SearchResultListEle
|
|||||||
), followLink('action'));
|
), followLink('action'));
|
||||||
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -2,4 +2,4 @@
|
|||||||
[object]="object"
|
[object]="object"
|
||||||
[status]="status"></ds-item-list-preview>
|
[status]="status"></ds-item-list-preview>
|
||||||
|
|
||||||
<ds-item-actions [object]="dso"></ds-item-actions>
|
<ds-item-actions [object]="dso" (processCompleted)="reloadedObject.emit($event.reloadedObject)"></ds-item-actions>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { waitForAsync, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
@@ -9,12 +9,11 @@ import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspa
|
|||||||
import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model';
|
import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model';
|
||||||
import { ItemSearchResultListElementSubmissionComponent } from './item-search-result-list-element-submission.component';
|
import { ItemSearchResultListElementSubmissionComponent } from './item-search-result-list-element-submission.component';
|
||||||
import { TruncatableService } from '../../../truncatable/truncatable.service';
|
import { TruncatableService } from '../../../truncatable/truncatable.service';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
let component: ItemSearchResultListElementSubmissionComponent;
|
let component: ItemSearchResultListElementSubmissionComponent;
|
||||||
let fixture: ComponentFixture<ItemSearchResultListElementSubmissionComponent>;
|
let fixture: ComponentFixture<ItemSearchResultListElementSubmissionComponent>;
|
||||||
|
|
||||||
const compIndex = 1;
|
|
||||||
|
|
||||||
const mockResultObject: ItemSearchResult = new ItemSearchResult();
|
const mockResultObject: ItemSearchResult = new ItemSearchResult();
|
||||||
mockResultObject.hitHighlights = {};
|
mockResultObject.hitHighlights = {};
|
||||||
|
|
||||||
@@ -75,4 +74,16 @@ describe('ItemMyDSpaceResultListElementComponent', () => {
|
|||||||
it('should have properly status', () => {
|
it('should have properly status', () => {
|
||||||
expect(component.status).toEqual(MyDspaceItemStatusType.ARCHIVED);
|
expect(component.status).toEqual(MyDspaceItemStatusType.ARCHIVED);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should forward item-actions processComplete event to reloadObject event emitter', fakeAsync(() => {
|
||||||
|
spyOn(component.reloadedObject, 'emit').and.callThrough();
|
||||||
|
const actionPayload: any = { reloadedObject: {}};
|
||||||
|
|
||||||
|
const actionsComponent = fixture.debugElement.query(By.css('ds-item-actions'));
|
||||||
|
actionsComponent.triggerEventHandler('processCompleted', actionPayload);
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(component.reloadedObject.emit).toHaveBeenCalledWith(actionPayload.reloadedObject);
|
||||||
|
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
@@ -4,6 +4,5 @@
|
|||||||
[object]="object"
|
[object]="object"
|
||||||
[showSubmitter]="showSubmitter"
|
[showSubmitter]="showSubmitter"
|
||||||
[status]="status"></ds-item-list-preview>
|
[status]="status"></ds-item-list-preview>
|
||||||
|
<ds-pool-task-actions id="actions" *ngIf="workflowitem" [object]="dso" (processCompleted)="this.reloadedObject.emit($event.reloadedObject)"></ds-pool-task-actions>
|
||||||
<ds-pool-task-actions *ngIf="workflowitem" [object]="dso"></ds-pool-task-actions>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { waitForAsync, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
@@ -15,12 +15,11 @@ import { TruncatableService } from '../../../truncatable/truncatable.service';
|
|||||||
import { VarDirective } from '../../../utils/var.directive';
|
import { VarDirective } from '../../../utils/var.directive';
|
||||||
import { LinkService } from '../../../../core/cache/builders/link.service';
|
import { LinkService } from '../../../../core/cache/builders/link.service';
|
||||||
import { getMockLinkService } from '../../../mocks/link-service.mock';
|
import { getMockLinkService } from '../../../mocks/link-service.mock';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
let component: PoolSearchResultListElementComponent;
|
let component: PoolSearchResultListElementComponent;
|
||||||
let fixture: ComponentFixture<PoolSearchResultListElementComponent>;
|
let fixture: ComponentFixture<PoolSearchResultListElementComponent>;
|
||||||
|
|
||||||
const compIndex = 1;
|
|
||||||
|
|
||||||
const mockResultObject: PoolTaskSearchResult = new PoolTaskSearchResult();
|
const mockResultObject: PoolTaskSearchResult = new PoolTaskSearchResult();
|
||||||
mockResultObject.hitHighlights = {};
|
mockResultObject.hitHighlights = {};
|
||||||
|
|
||||||
@@ -99,4 +98,15 @@ describe('PoolSearchResultListElementComponent', () => {
|
|||||||
it('should have properly status', () => {
|
it('should have properly status', () => {
|
||||||
expect(component.status).toEqual(MyDspaceItemStatusType.WAITING_CONTROLLER);
|
expect(component.status).toEqual(MyDspaceItemStatusType.WAITING_CONTROLLER);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should forward pool-task-actions processCompleted event to the reloadedObject event emitter', fakeAsync(() => {
|
||||||
|
spyOn(component.reloadedObject, 'emit').and.callThrough();
|
||||||
|
const actionPayload: any = { reloadedObject: {}};
|
||||||
|
const actionsComponents = fixture.debugElement.query(By.css('ds-pool-task-actions'));
|
||||||
|
actionsComponents.triggerEventHandler('processCompleted', actionPayload);
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(component.reloadedObject.emit).toHaveBeenCalledWith(actionPayload.reloadedObject);
|
||||||
|
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
@@ -63,4 +63,5 @@ export class PoolSearchResultListElementComponent extends SearchResultListElemen
|
|||||||
), followLink('action'));
|
), followLink('action'));
|
||||||
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
[object]="object"
|
[object]="object"
|
||||||
[status]="status"></ds-item-list-preview>
|
[status]="status"></ds-item-list-preview>
|
||||||
|
|
||||||
<ds-workflowitem-actions [object]="dso"></ds-workflowitem-actions>
|
<ds-workflowitem-actions [object]="dso" (processCompleted)="reloadedObject.emit($event.reloadedObject)"></ds-workflowitem-actions>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ds-loading
|
<ds-loading
|
||||||
*ngIf="!(item$ | async)"
|
*ngIf="!(item$ | async)"
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { waitForAsync, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
@@ -15,12 +15,11 @@ import { WorkflowItemSearchResult } from '../../../object-collection/shared/work
|
|||||||
import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils';
|
import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils';
|
||||||
import { TruncatableService } from '../../../truncatable/truncatable.service';
|
import { TruncatableService } from '../../../truncatable/truncatable.service';
|
||||||
import { WorkflowItemSearchResultListElementComponent } from './workflow-item-search-result-list-element.component';
|
import { WorkflowItemSearchResultListElementComponent } from './workflow-item-search-result-list-element.component';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
let component: WorkflowItemSearchResultListElementComponent;
|
let component: WorkflowItemSearchResultListElementComponent;
|
||||||
let fixture: ComponentFixture<WorkflowItemSearchResultListElementComponent>;
|
let fixture: ComponentFixture<WorkflowItemSearchResultListElementComponent>;
|
||||||
|
|
||||||
const compIndex = 1;
|
|
||||||
|
|
||||||
const mockResultObject: WorkflowItemSearchResult = new WorkflowItemSearchResult();
|
const mockResultObject: WorkflowItemSearchResult = new WorkflowItemSearchResult();
|
||||||
mockResultObject.hitHighlights = {};
|
mockResultObject.hitHighlights = {};
|
||||||
|
|
||||||
@@ -96,4 +95,16 @@ describe('WorkflowItemSearchResultListElementComponent', () => {
|
|||||||
it('should have properly status', () => {
|
it('should have properly status', () => {
|
||||||
expect(component.status).toEqual(MyDspaceItemStatusType.WORKFLOW);
|
expect(component.status).toEqual(MyDspaceItemStatusType.WORKFLOW);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should forward workflowitem-actions processCompleted event to the reloadedObject event emitter', fakeAsync(() => {
|
||||||
|
spyOn(component.reloadedObject, 'emit').and.callThrough();
|
||||||
|
const actionPayload: any = { reloadedObject: {}};
|
||||||
|
|
||||||
|
const actionsComponent = fixture.debugElement.query(By.css('ds-workflowitem-actions'));
|
||||||
|
actionsComponent.triggerEventHandler('processCompleted', actionPayload);
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(component.reloadedObject.emit).toHaveBeenCalledWith(actionPayload.reloadedObject);
|
||||||
|
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
[object]="object"
|
[object]="object"
|
||||||
[status]="status"></ds-item-list-preview>
|
[status]="status"></ds-item-list-preview>
|
||||||
|
|
||||||
<ds-workspaceitem-actions [object]="dso"></ds-workspaceitem-actions>
|
<ds-workspaceitem-actions [object]="dso" (processCompleted)="reloadedObject.emit($event.reloadedObject)"></ds-workspaceitem-actions>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ds-loading
|
<ds-loading
|
||||||
*ngIf="!(item$ | async)"
|
*ngIf="!(item$ | async)"
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { waitForAsync, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
@@ -15,12 +15,11 @@ import { WorkflowItemSearchResult } from '../../../object-collection/shared/work
|
|||||||
import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils';
|
import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils';
|
||||||
import { TruncatableService } from '../../../truncatable/truncatable.service';
|
import { TruncatableService } from '../../../truncatable/truncatable.service';
|
||||||
import { WorkspaceItemSearchResultListElementComponent } from './workspace-item-search-result-list-element.component';
|
import { WorkspaceItemSearchResultListElementComponent } from './workspace-item-search-result-list-element.component';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
let component: WorkspaceItemSearchResultListElementComponent;
|
let component: WorkspaceItemSearchResultListElementComponent;
|
||||||
let fixture: ComponentFixture<WorkspaceItemSearchResultListElementComponent>;
|
let fixture: ComponentFixture<WorkspaceItemSearchResultListElementComponent>;
|
||||||
|
|
||||||
const compIndex = 1;
|
|
||||||
|
|
||||||
const mockResultObject: WorkflowItemSearchResult = new WorkflowItemSearchResult();
|
const mockResultObject: WorkflowItemSearchResult = new WorkflowItemSearchResult();
|
||||||
mockResultObject.hitHighlights = {};
|
mockResultObject.hitHighlights = {};
|
||||||
|
|
||||||
@@ -95,4 +94,17 @@ describe('WorkspaceItemSearchResultListElementComponent', () => {
|
|||||||
it('should have properly status', () => {
|
it('should have properly status', () => {
|
||||||
expect(component.status).toEqual(MyDspaceItemStatusType.WORKSPACE);
|
expect(component.status).toEqual(MyDspaceItemStatusType.WORKSPACE);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should forward workspaceitem-actions processCompleted event to the reloadedObject event emitter', fakeAsync(() => {
|
||||||
|
|
||||||
|
spyOn(component.reloadedObject, 'emit').and.callThrough();
|
||||||
|
const actionPayload: any = { reloadedObject: {}};
|
||||||
|
|
||||||
|
const actionsComponent = fixture.debugElement.query(By.css('ds-workspaceitem-actions'));
|
||||||
|
actionsComponent.triggerEventHandler('processCompleted', actionPayload);
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(component.reloadedObject.emit).toHaveBeenCalledWith(actionPayload.reloadedObject);
|
||||||
|
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
@@ -22,7 +22,9 @@
|
|||||||
[importConfig]="importConfig"
|
[importConfig]="importConfig"
|
||||||
(importObject)="importObject.emit($event)"></ds-importable-list-item-control>
|
(importObject)="importObject.emit($event)"></ds-importable-list-item-control>
|
||||||
<ds-listable-object-component-loader [object]="object" [viewMode]="viewMode" [index]="i" [context]="context" [linkType]="linkType"
|
<ds-listable-object-component-loader [object]="object" [viewMode]="viewMode" [index]="i" [context]="context" [linkType]="linkType"
|
||||||
[listID]="selectionConfig?.listId"></ds-listable-object-component-loader>
|
[listID]="selectionConfig?.listId"
|
||||||
|
(contentChange)="contentChange.emit()"
|
||||||
|
></ds-listable-object-component-loader>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ds-pagination>
|
</ds-pagination>
|
||||||
|
@@ -76,6 +76,11 @@ export class ObjectListComponent {
|
|||||||
*/
|
*/
|
||||||
@Input() importConfig: { importLabel: string };
|
@Input() importConfig: { importLabel: string };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit when one of the listed object has changed.
|
||||||
|
*/
|
||||||
|
@Output() contentChange = new EventEmitter<any>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current listable objects
|
* The current listable objects
|
||||||
*/
|
*/
|
||||||
|
@@ -12,7 +12,6 @@ import { Metadata } from '../../../core/shared/metadata.utils';
|
|||||||
selector: 'ds-search-result-list-element',
|
selector: 'ds-search-result-list-element',
|
||||||
template: ``
|
template: ``
|
||||||
})
|
})
|
||||||
|
|
||||||
export class SearchResultListElementComponent<T extends SearchResult<K>, K extends DSpaceObject> extends AbstractListableElementComponent<T> implements OnInit {
|
export class SearchResultListElementComponent<T extends SearchResult<K>, K extends DSpaceObject> extends AbstractListableElementComponent<T> implements OnInit {
|
||||||
/**
|
/**
|
||||||
* The DSpaceObject of the search result
|
* The DSpaceObject of the search result
|
||||||
|
@@ -7,7 +7,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
|||||||
import { SearchFilterService } from '../../../core/shared/search/search-filter.service';
|
import { SearchFilterService } from '../../../core/shared/search/search-filter.service';
|
||||||
import { SearchFiltersComponent } from './search-filters.component';
|
import { SearchFiltersComponent } from './search-filters.component';
|
||||||
import { SearchService } from '../../../core/shared/search/search.service';
|
import { SearchService } from '../../../core/shared/search/search.service';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf, Subject } from 'rxjs';
|
||||||
import { SEARCH_CONFIG_SERVICE } from '../../../+my-dspace-page/my-dspace-page.component';
|
import { SEARCH_CONFIG_SERVICE } from '../../../+my-dspace-page/my-dspace-page.component';
|
||||||
import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub';
|
import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub';
|
||||||
|
|
||||||
@@ -66,4 +66,26 @@ describe('SearchFiltersComponent', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when refreshSearch observable is present and emit events', () => {
|
||||||
|
|
||||||
|
let refreshFiltersEmitter: Subject<any>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(comp, 'initFilters').and.callFake(() => { /****/});
|
||||||
|
|
||||||
|
refreshFiltersEmitter = new Subject();
|
||||||
|
comp.refreshFilters = refreshFiltersEmitter.asObservable();
|
||||||
|
comp.ngOnInit();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reinitialize search filters', () => {
|
||||||
|
|
||||||
|
expect(comp.initFilters).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
refreshFiltersEmitter.next();
|
||||||
|
|
||||||
|
expect(comp.initFilters).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, Inject, Input, OnInit } from '@angular/core';
|
import { Component, Inject, Input, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { map, switchMap } from 'rxjs/operators';
|
import { map, switchMap } from 'rxjs/operators';
|
||||||
@@ -12,17 +12,19 @@ import { getFirstSucceededRemoteData } from '../../../core/shared/operators';
|
|||||||
import { SEARCH_CONFIG_SERVICE } from '../../../+my-dspace-page/my-dspace-page.component';
|
import { SEARCH_CONFIG_SERVICE } from '../../../+my-dspace-page/my-dspace-page.component';
|
||||||
import { currentPath } from '../../utils/route.utils';
|
import { currentPath } from '../../utils/route.utils';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
import { hasValue } from '../../empty.util';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search-filters',
|
selector: 'ds-search-filters',
|
||||||
styleUrls: ['./search-filters.component.scss'],
|
styleUrls: ['./search-filters.component.scss'],
|
||||||
templateUrl: './search-filters.component.html',
|
templateUrl: './search-filters.component.html',
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component represents the part of the search sidebar that contains filters.
|
* This component represents the part of the search sidebar that contains filters.
|
||||||
*/
|
*/
|
||||||
export class SearchFiltersComponent implements OnInit {
|
export class SearchFiltersComponent implements OnInit, OnDestroy {
|
||||||
/**
|
/**
|
||||||
* An observable containing configuration about which filters are shown and how they are shown
|
* An observable containing configuration about which filters are shown and how they are shown
|
||||||
*/
|
*/
|
||||||
@@ -39,11 +41,18 @@ export class SearchFiltersComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
@Input() inPlaceSearch;
|
@Input() inPlaceSearch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits when the search filters values may be stale, and so they must be refreshed.
|
||||||
|
*/
|
||||||
|
@Input() refreshFilters: Observable<any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Link to the search page
|
* Link to the search page
|
||||||
*/
|
*/
|
||||||
searchLink: string;
|
searchLink: string;
|
||||||
|
|
||||||
|
subs = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize instance variables
|
* Initialize instance variables
|
||||||
* @param {SearchService} searchService
|
* @param {SearchService} searchService
|
||||||
@@ -58,9 +67,12 @@ export class SearchFiltersComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.filters = this.searchConfigService.searchOptions.pipe(
|
|
||||||
switchMap((options) => this.searchService.getConfig(options.scope, options.configuration).pipe(getFirstSucceededRemoteData())),
|
this.initFilters();
|
||||||
);
|
|
||||||
|
if (this.refreshFilters) {
|
||||||
|
this.subs.push(this.refreshFilters.subscribe(() => this.initFilters()));
|
||||||
|
}
|
||||||
|
|
||||||
this.clearParams = this.searchConfigService.getCurrentFrontendFilters().pipe(map((filters) => {
|
this.clearParams = this.searchConfigService.getCurrentFrontendFilters().pipe(map((filters) => {
|
||||||
Object.keys(filters).forEach((f) => filters[f] = null);
|
Object.keys(filters).forEach((f) => filters[f] = null);
|
||||||
@@ -69,6 +81,12 @@ export class SearchFiltersComponent implements OnInit {
|
|||||||
this.searchLink = this.getSearchLink();
|
this.searchLink = this.getSearchLink();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initFilters() {
|
||||||
|
this.filters = this.searchConfigService.searchOptions.pipe(
|
||||||
|
switchMap((options) => this.searchService.getConfig(options.scope, options.configuration).pipe(getFirstSucceededRemoteData())),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {string} The base path to the search page, or the current page when inPlaceSearch is true
|
* @returns {string} The base path to the search page, or the current page when inPlaceSearch is true
|
||||||
*/
|
*/
|
||||||
@@ -85,4 +103,12 @@ export class SearchFiltersComponent implements OnInit {
|
|||||||
trackUpdate(index, config: SearchFilterConfig) {
|
trackUpdate(index, config: SearchFilterConfig) {
|
||||||
return config ? config.name : undefined;
|
return config ? config.name : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.subs.forEach((sub) => {
|
||||||
|
if (hasValue(sub)) {
|
||||||
|
sub.unsubscribe();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
<ds-view-mode-switch *ngIf="showViewModes" [viewModeList]="viewModeList" class="d-none d-md-block"></ds-view-mode-switch>
|
<ds-view-mode-switch *ngIf="showViewModes" [viewModeList]="viewModeList" class="d-none d-md-block"></ds-view-mode-switch>
|
||||||
<div class="sidebar-content">
|
<div class="sidebar-content">
|
||||||
<ds-search-switch-configuration [inPlaceSearch]="inPlaceSearch" *ngIf="configurationList" [configurationList]="configurationList"></ds-search-switch-configuration>
|
<ds-search-switch-configuration [inPlaceSearch]="inPlaceSearch" *ngIf="configurationList" [configurationList]="configurationList"></ds-search-switch-configuration>
|
||||||
<ds-search-filters [inPlaceSearch]="inPlaceSearch"></ds-search-filters>
|
<ds-search-filters [refreshFilters]="refreshFilters" [inPlaceSearch]="inPlaceSearch"></ds-search-filters>
|
||||||
<ds-search-settings></ds-search-settings>
|
<ds-search-settings></ds-search-settings>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
|
|
||||||
import { SearchConfigurationOption } from '../search-switch-configuration/search-configuration-option.model';
|
import { SearchConfigurationOption } from '../search-switch-configuration/search-configuration-option.model';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders a simple item page.
|
* This component renders a simple item page.
|
||||||
@@ -44,6 +45,11 @@ export class SearchSidebarComponent {
|
|||||||
*/
|
*/
|
||||||
@Input() inPlaceSearch;
|
@Input() inPlaceSearch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits when the search filters values may be stale, and so they must be refreshed.
|
||||||
|
*/
|
||||||
|
@Input() refreshFilters: Observable<any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emits event when the user clicks a button to open or close the sidebar
|
* Emits event when the user clicks a button to open or close the sidebar
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user