From 305c4ce882e08202b782f20200fa5d0ffd7ee8ed Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 24 Feb 2020 17:13:12 +0100 Subject: [PATCH 1/7] 68954: Display claimed task actions depending on config from REST API Conflicts: src/app/core/core.module.ts src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.html --- src/app/core/core.module.ts | 4 + .../core/data/workflow-action-data.service.ts | 40 +++++++++ ...normalized-workflow-action-object.model.ts | 23 +++++ .../models/workflow-action-object.model.ts | 19 +++++ ...claimed-task-actions-abstract.component.ts | 28 ++++++ ...laimed-task-actions-approve.component.html | 8 +- .../claimed-task-actions-approve.component.ts | 41 ++++----- .../claimed-task-actions.component.html | 33 +++---- .../claimed-task-actions.component.ts | 62 ++++---------- ...-task-actions-edit-metadata.component.html | 7 ++ ...-task-actions-edit-metadata.component.scss | 0 ...sk-actions-edit-metadata.component.spec.ts | 65 ++++++++++++++ ...ed-task-actions-edit-metadata.component.ts | 20 +++++ ...claimed-task-actions-reject.component.html | 14 +-- .../claimed-task-actions-reject.component.ts | 45 +++++----- ...task-actions-return-to-pool.component.html | 8 +- ...d-task-actions-return-to-pool.component.ts | 43 +++++----- .../claimed-task-actions-decorator.ts | 23 +++++ ...claimed-task-actions-loader.component.html | 1 + .../claimed-task-actions-loader.component.ts | 85 +++++++++++++++++++ .../claimed-task-actions.directive.ts | 11 +++ src/app/shared/shared.module.ts | 14 ++- 22 files changed, 452 insertions(+), 142 deletions(-) create mode 100644 src/app/core/data/workflow-action-data.service.ts create mode 100644 src/app/core/tasks/models/normalized-workflow-action-object.model.ts create mode 100644 src/app/core/tasks/models/workflow-action-object.model.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/abstract/claimed-task-actions-abstract.component.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.html create mode 100644 src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.scss create mode 100644 src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.spec.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.html create mode 100644 src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions.directive.ts diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 82edaed96b2..ffa2852ddc4 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -142,6 +142,8 @@ import { PoolTask } from './tasks/models/pool-task-object.model'; import { TaskObject } from './tasks/models/task-object.model'; import { PoolTaskDataService } from './tasks/pool-task-data.service'; import { TaskResponseParsingService } from './tasks/task-response-parsing.service'; +import { WorkflowActionDataService } from './data/workflow-action-data.service'; +import { NormalizedWorkflowAction } from './tasks/models/normalized-workflow-action-object.model'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -257,6 +259,7 @@ const PROVIDERS = [ LookupRelationService, LicenseDataService, ItemTypeDataService, + WorkflowActionDataService, // register AuthInterceptor as HttpInterceptor { provide: HTTP_INTERCEPTORS, @@ -305,6 +308,7 @@ export const models = ItemType, ExternalSource, ExternalSourceEntry, + NormalizedWorkflowAction ]; @NgModule({ diff --git a/src/app/core/data/workflow-action-data.service.ts b/src/app/core/data/workflow-action-data.service.ts new file mode 100644 index 00000000000..17b861e1af0 --- /dev/null +++ b/src/app/core/data/workflow-action-data.service.ts @@ -0,0 +1,40 @@ +import { DataService } from './data.service'; +import { WorkflowAction } from '../tasks/models/workflow-action-object.model'; +import { RequestService } from './request.service'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; +import { Store } from '@ngrx/store'; +import { CoreState } from '../core.reducers'; +import { ObjectCacheService } from '../cache/object-cache.service'; +import { HALEndpointService } from '../shared/hal-endpoint.service'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { HttpClient } from '@angular/common/http'; +import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; +import { FindListOptions } from './request.models'; +import { Observable } from 'rxjs/internal/Observable'; +import { Injectable } from '@angular/core'; + +/** + * A service responsible for fetching/sending data from/to the REST API on the workflowactions endpoint + */ +@Injectable() +export class WorkflowActionDataService extends DataService { + protected linkPath = 'workflowactions'; + + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected dataBuildService: NormalizedObjectBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: DefaultChangeAnalyzer) { + super(); + } + + getBrowseEndpoint(options: FindListOptions, linkPath?: string): Observable { + return this.halService.getEndpoint(this.linkPath); + } +} diff --git a/src/app/core/tasks/models/normalized-workflow-action-object.model.ts b/src/app/core/tasks/models/normalized-workflow-action-object.model.ts new file mode 100644 index 00000000000..a5d1754a4c4 --- /dev/null +++ b/src/app/core/tasks/models/normalized-workflow-action-object.model.ts @@ -0,0 +1,23 @@ +import { autoserialize, inheritSerialization } from 'cerialize'; +import { mapsTo } from '../../cache/builders/build-decorators'; +import { WorkflowAction } from './workflow-action-object.model'; +import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model'; + +/** + * A normalized model class for a WorkflowAction + */ +@mapsTo(WorkflowAction) +@inheritSerialization(NormalizedDSpaceObject) +export class NormalizedWorkflowAction extends NormalizedDSpaceObject { + /** + * The workflow action's identifier + */ + @autoserialize + id: string; + + /** + * The options available for this workflow action + */ + @autoserialize + options: string[]; +} diff --git a/src/app/core/tasks/models/workflow-action-object.model.ts b/src/app/core/tasks/models/workflow-action-object.model.ts new file mode 100644 index 00000000000..57d90354f08 --- /dev/null +++ b/src/app/core/tasks/models/workflow-action-object.model.ts @@ -0,0 +1,19 @@ +import { ResourceType } from '../../shared/resource-type'; +import { DSpaceObject } from '../../shared/dspace-object.model'; + +/** + * A model class for a WorkflowAction + */ +export class WorkflowAction extends DSpaceObject { + static type = new ResourceType('workflowaction'); + + /** + * The workflow action's identifier + */ + id: string; + + /** + * The options available for this workflow action + */ + options: string[]; +} diff --git a/src/app/shared/mydspace-actions/claimed-task/abstract/claimed-task-actions-abstract.component.ts b/src/app/shared/mydspace-actions/claimed-task/abstract/claimed-task-actions-abstract.component.ts new file mode 100644 index 00000000000..761f0883892 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/abstract/claimed-task-actions-abstract.component.ts @@ -0,0 +1,28 @@ +import { EventEmitter, Input, Output } from '@angular/core'; +import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; +import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; + +/** + * Abstract component for rendering a claimed task's action + */ +export abstract class ClaimedTaskActionsAbstractComponent { + /** + * The Claimed Task to display an action for + */ + @Input() object: ClaimedTask; + + /** + * Emits the success or failure of a processed action + */ + @Output() processCompleted: EventEmitter = new EventEmitter(); + + /** + * A boolean representing if the operation is pending + */ + processing$ = new BehaviorSubject(false); + + /** + * Method called when the action's button is clicked + */ + abstract process(); +} diff --git a/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.html b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.html index 3c41fdbb07c..78e56739221 100644 --- a/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.html +++ b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.html @@ -1,8 +1,8 @@ diff --git a/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.ts b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.ts index 8e7c0dab606..13cc50b6c05 100644 --- a/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.ts @@ -1,32 +1,33 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component } from '@angular/core'; +import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component'; +import { ProcessTaskResponse } from '../../../../core/tasks/models/process-task-response'; +import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator'; +import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service'; +@rendersWorkflowTaskOption('submit_approve') @Component({ selector: 'ds-claimed-task-actions-approve', styleUrls: ['./claimed-task-actions-approve.component.scss'], templateUrl: './claimed-task-actions-approve.component.html', }) +/** + * Component for displaying and processing the approve action on a workflow task item + */ +export class ClaimedTaskActionsApproveComponent extends ClaimedTaskActionsAbstractComponent { -export class ClaimedTaskActionsApproveComponent { - - /** - * A boolean representing if a reject operation is pending - */ - @Input() processingApprove: boolean; - - /** - * CSS classes to append to reject button - */ - @Input() wrapperClass: string; - - /** - * An event fired when a approve action is confirmed. - */ - @Output() approve: EventEmitter = new EventEmitter(); + constructor(protected claimedTaskService: ClaimedTaskDataService) { + super(); + } /** - * Emit approve event + * Approve the task */ - confirmApprove() { - this.approve.emit(); + process() { + this.processing$.next(true); + this.claimedTaskService.approveTask(this.object.id) + .subscribe((res: ProcessTaskResponse) => { + this.processing$.next(false); + this.processCompleted.emit(res.hasSucceeded); + }); } } diff --git a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.html b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.html index df8fb0eae7a..aa569bbfc8c 100644 --- a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.html +++ b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.html @@ -1,20 +1,13 @@ - - - {{'submission.workflow.tasks.claimed.edit' | translate}} - - - - - - - + +
+ + + + +
+
diff --git a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.ts b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.ts index 81d24fa1d7b..c3802a38467 100644 --- a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.ts @@ -1,13 +1,12 @@ import { Component, Injector, Input, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { BehaviorSubject, Observable } from 'rxjs'; +import { Observable } from 'rxjs'; import { filter, map } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service'; import { ClaimedTask } from '../../../core/tasks/models/claimed-task-object.model'; -import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response'; import { isNotUndefined } from '../../empty.util'; import { WorkflowItem } from '../../../core/submission/models/workflowitem.model'; import { RemoteData } from '../../../core/data/remote-data'; @@ -15,6 +14,9 @@ import { MyDSpaceActionsComponent } from '../mydspace-actions'; import { NotificationsService } from '../../notifications/notifications.service'; import { RequestService } from '../../../core/data/request.service'; import { SearchService } from '../../../core/shared/search/search.service'; +import { WorkflowAction } from '../../../core/tasks/models/workflow-action-object.model'; +import { WorkflowActionDataService } from '../../../core/data/workflow-action-data.service'; +import { WORKFLOW_TASK_OPTION_RETURN } from './return-to-pool/claimed-task-actions-return-to-pool.component'; /** * This component represents actions related to ClaimedTask object. @@ -37,19 +39,15 @@ export class ClaimedTaskActionsComponent extends MyDSpaceActionsComponent; /** - * A boolean representing if an approve operation is pending + * The workflow action available for this task */ - public processingApprove$ = new BehaviorSubject(false); + public actionRD$: Observable>; /** - * A boolean representing if a reject operation is pending + * The option used to render the "return to pool" component + * Every claimed task contains this option */ - public processingReject$ = new BehaviorSubject(false); - - /** - * A boolean representing if a return to pool operation is pending - */ - public processingReturnToPool$ = new BehaviorSubject(false); + public returnToPoolOption = WORKFLOW_TASK_OPTION_RETURN; /** * Initialize instance variables @@ -60,13 +58,15 @@ export class ClaimedTaskActionsComponent extends MyDSpaceActionsComponent { - this.processingApprove$.next(false); - this.handleActionResponse(res.hasSucceeded); - }); - } - - /** - * Reject the task. - */ - reject(reason) { - this.processingReject$.next(true); - this.objectDataService.rejectTask(reason, this.object.id) - .subscribe((res: ProcessTaskResponse) => { - this.processingReject$.next(false); - this.handleActionResponse(res.hasSucceeded); - }); - } - - /** - * Return task to the pool. + * Init the WorkflowAction + * + * @param object */ - returnToPool() { - this.processingReturnToPool$.next(true); - this.objectDataService.returnToPoolTask(this.object.id) - .subscribe((res: ProcessTaskResponse) => { - this.processingReturnToPool$.next(false); - this.handleActionResponse(res.hasSucceeded); - }); + initAction(object: ClaimedTask) { + this.actionRD$ = this.workflowActionService.findById(object.action); } } diff --git a/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.html b/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.html new file mode 100644 index 00000000000..4a42378f7e0 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.html @@ -0,0 +1,7 @@ + + {{'submission.workflow.tasks.claimed.edit' | translate}} + diff --git a/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.scss b/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.spec.ts new file mode 100644 index 00000000000..cd1f687544b --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.spec.ts @@ -0,0 +1,65 @@ +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; + +import { ClaimedTaskActionsEditMetadataComponent } from './claimed-task-actions-approve.component'; +import { MockTranslateLoader } from '../../../mocks/mock-translate-loader'; + +let component: ClaimedTaskActionsEditMetadataComponent; +let fixture: ComponentFixture; + +describe('ClaimedTaskActionsApproveComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }) + ], + declarations: [ClaimedTaskActionsEditMetadataComponent], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ClaimedTaskActionsEditMetadataComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ClaimedTaskActionsEditMetadataComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + afterEach(() => { + fixture = null; + component = null; + }); + + it('should display approve button', () => { + const btn = fixture.debugElement.query(By.css('.btn-success')); + + expect(btn).toBeDefined(); + }); + + it('should display spin icon when approve is pending', () => { + component.processingApprove = true; + fixture.detectChanges(); + + const span = fixture.debugElement.query(By.css('.btn-success .fa-spin')); + + expect(span).toBeDefined(); + }); + + it('should emit approve event', () => { + spyOn(component.approve, 'emit'); + + component.confirmApprove(); + fixture.detectChanges(); + + expect(component.approve.emit).toHaveBeenCalled(); + }); + +}); diff --git a/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.ts b/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.ts new file mode 100644 index 00000000000..c260459e706 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.ts @@ -0,0 +1,20 @@ +import { Component } from '@angular/core'; +import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component'; +import { ProcessTaskResponse } from '../../../../core/tasks/models/process-task-response'; +import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator'; +import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service'; + +@rendersWorkflowTaskOption('submit_edit_metadata') +@Component({ + selector: 'ds-claimed-task-actions-edit-metadata', + styleUrls: ['./claimed-task-actions-edit-metadata.component.scss'], + templateUrl: './claimed-task-actions-edit-metadata.component.html', +}) +/** + * Component for displaying the edit metadata action on a workflow task item + */ +export class ClaimedTaskActionsEditMetadataComponent extends ClaimedTaskActionsAbstractComponent { + process() { + // Nothing needs to happen for the edit-metadata button, it simply renders a link to another page + } +} diff --git a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.html b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.html index 91edee66bd5..975b964c1b2 100644 --- a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.html +++ b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.html @@ -1,10 +1,10 @@

@@ -21,17 +21,17 @@