diff --git a/demo-shell/src/app/app.module.ts b/demo-shell/src/app/app.module.ts
index adbb66ad7c..376d429c25 100644
--- a/demo-shell/src/app/app.module.ts
+++ b/demo-shell/src/app/app.module.ts
@@ -64,7 +64,7 @@ import { ContentModule } from '@alfresco/adf-content-services';
import { InsightsModule } from '@alfresco/adf-insights';
import { ProcessModule } from '@alfresco/adf-process-services';
import { AuthBearerInterceptor } from './services';
-import { ProcessServicesCloudModule, GroupCloudModule } from '@alfresco/adf-process-services-cloud';
+import { ProcessServicesCloudModule, GroupCloudModule, TaskDirectiveModule } from '@alfresco/adf-process-services-cloud';
import { TreeViewSampleComponent } from './components/tree-view/tree-view-sample.component';
import { CloudLayoutComponent } from './components/app-layout/cloud/cloud-layout.component';
import { AppsCloudDemoComponent } from './components/app-layout/cloud/apps-cloud-demo.component';
@@ -102,7 +102,9 @@ import { NestedMenuPositionDirective } from './components/app-layout/cloud/direc
ThemePickerModule,
ChartsModule,
MonacoEditorModule.forRoot(),
- GroupCloudModule
+ ProcessServicesCloudModule,
+ GroupCloudModule,
+ TaskDirectiveModule
],
declarations: [
AppComponent,
diff --git a/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.html b/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.html
index c3f4a4d399..2a72d8d545 100644
--- a/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.html
+++ b/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.html
@@ -1,12 +1,13 @@
-
-
-
Simple page to show the taskId: {{ taskId }} of the app: {{ appName }}
-
-
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.scss b/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.scss
index e69de29bb2..e97ca949e4 100644
--- a/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.scss
+++ b/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.scss
@@ -0,0 +1,20 @@
+
+.adf {
+
+ &-task-detail-container {
+ display: flex;
+ }
+
+ &-task-tiitle {
+ margin-left:15px;
+ }
+
+ &-task-control {
+ width:70%;
+ }
+
+ &-demop-card-container {
+ width:30%;
+ font-family: inherit;
+ }
+}
diff --git a/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.ts b/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.ts
index c5ade39624..6838200b2e 100644
--- a/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.ts
+++ b/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.ts
@@ -15,20 +15,26 @@
* limitations under the License.
*/
-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
+import { TaskCloudService, TaskDetailsCloudModel } from '@alfresco/adf-process-services-cloud';
@Component({
templateUrl: './task-details-cloud-demo.component.html',
styleUrls: ['./task-details-cloud-demo.component.scss']
})
-export class TaskDetailsCloudDemoComponent {
+export class TaskDetailsCloudDemoComponent implements OnInit {
+ taskDetails: TaskDetailsCloudModel;
taskId: string;
appName: string;
readOnly = false;
- constructor(private route: ActivatedRoute, private router: Router) {
+ constructor(
+ private route: ActivatedRoute,
+ private router: Router,
+ private taskCloudService: TaskCloudService
+ ) {
this.route.params.subscribe((params) => {
this.taskId = params.taskId;
});
@@ -37,8 +43,30 @@ export class TaskDetailsCloudDemoComponent {
});
}
- onGoBack() {
- this.router.navigate([`/cloud/${this.appName}/`]);
+ ngOnInit() {
+ this.loadTaskDetailsById(this.appName, this.taskId);
+ }
+ loadTaskDetailsById(appName: string, taskId: string): any {
+ this.taskCloudService.getTaskById(appName, taskId).subscribe(
+ (taskDetails) => {
+ this.taskDetails = taskDetails;
+ });
+ }
+
+ isTaskValid() {
+ return this.appName && this.taskId;
+ }
+
+ canCompleteTask() {
+ return this.taskDetails && this.taskCloudService.canCompleteTask(this.taskDetails);
+ }
+
+ goBack() {
+ this.router.navigate([`/cloud/${this.appName}/`]);
+ }
+
+ onCompletedTask(evt: any) {
+ this.goBack();
}
}
diff --git a/docs/process-services-cloud/complete-task.directive.md b/docs/process-services-cloud/complete-task.directive.md
new file mode 100644
index 0000000000..0ad4ad5856
--- /dev/null
+++ b/docs/process-services-cloud/complete-task.directive.md
@@ -0,0 +1,26 @@
+---
+Title: Complete Cloud Task
+Added: v3.1.0
+Status: Experimental
+Last reviewed: 2019-02-28
+---
+
+# [Complete task directive](../../lib/process-services-cloud/src/lib/task/task-header/directives/complete-task.directive.ts "Defined in complete-task.directive.ts")
+
+Complete a task
+
+## Basic Usage
+
+```html
+
+```
+## Class members
+
+### Properties
+
+| Name | Type | Default value | Description |
+| ---- | ---- | ------------- | ----------- |
+| taskId | `string` | empty |(Required) The id of the task. |
+| appName | `string` | empty | (Required) The name of the application. |
+| success | `EventEmitter` | empty | Emitted when the task is completed. |
+| error | `EventEmitter` | empty | Emitted when the task cannot be completed. |
\ No newline at end of file
diff --git a/docs/process-services-cloud/task-cloud.service.md b/docs/process-services-cloud/task-cloud.service.md
new file mode 100644
index 0000000000..3c4a8968ad
--- /dev/null
+++ b/docs/process-services-cloud/task-cloud.service.md
@@ -0,0 +1,57 @@
+---
+Title: Task Cloud Service
+Added: v3.1.0
+Status: Experimental
+Last reviewed: 2019-02-28
+---
+
+# [Task Cloud Service](../../lib/process-services-cloud/src/lib/task/task-header/services/task-cloud.service.ts "Defined in task-cloud.service.ts")
+
+Manage task cloud.
+
+## Class members
+
+### Methods
+
+- **completeTask**(appName: `string`, taskId: `string`)
+ Complete a task
+ - _appName:_ `string` - Name of the app
+ - _taskId:_ `string` - ID of the task to complete
+
+- **canCompleteTask**(taskDetails: [`TaskDetailsCloudModel`](../../lib/process-services-cloud/src/lib/task/start-filters/models/task-details-cloud.model.ts))
+ Validate if a task can be completed.
+ - _taskDetails:_ [`TaskDetailsCloudModel`](../../lib/process-services-cloud/src/lib/task/start-filters/models/task-details-cloud.model.ts) - Task details object
+
+- **claimTask**(appName: `string`, taskId: `string`, assignee: `string`): `any`
+ Claims a task for an assignee.
+ - _appName:_ `string` - Name of the app
+ - _taskId:_ `string` - ID of the task to claim
+ - _assignee:_ `string` - User to assign the task to
+ - **Returns** `any` - Details of the claimed task
+- **getTaskById**(appName: `string`, taskId: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TaskDetailsCloudModel`](../../lib/process-services-cloud/src/lib/task/start-task/models/task-details-cloud.model.ts)`>`
+ Gets details of a task.
+ - _appName:_ `string` - Name of the app
+ - _taskId:_ `string` - ID of the task whose details you want
+ - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TaskDetailsCloudModel`](../../lib/process-services-cloud/src/lib/task/start-task/models/task-details-cloud.model.ts)`>` - Task details
+- **unclaimTask**(appName: `string`, taskId: `string`): `any`
+ Un-claims a task.
+ - _appName:_ `string` - Name of the app
+ - _taskId:_ `string` - ID of the task to unclaim
+ - **Returns** `any` - Details of the task that was unclaimed
+- **updateTask**(appName: `string`, taskId: `string`, updatePayload: `any`): `any`
+ Updates the details (name, description, due date) for a task.
+ - _appName:_ `string` - Name of the app
+ - _taskId:_ `string` - ID of the task to update
+ - _updatePayload:_ `any` - Data to update the task
+ - **Returns** `any` - Updated task details
+
+## Details
+
+The methods work in much the same way as the equivalent methods in the
+[Tasklist service](../process-services/tasklist.service.md)
+but they use the cloud variants of the classes for return values. See the
+[Tasklist service](../process-services/tasklist.service.md) page for usage examples.
+
+## See also
+
+- [Tasklist service](../process-services/tasklist.service.md)
\ No newline at end of file
diff --git a/lib/process-services-cloud/src/lib/task/directives/complete-task.directive.spec.ts b/lib/process-services-cloud/src/lib/task/directives/complete-task.directive.spec.ts
new file mode 100644
index 0000000000..0d1fdc413c
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/task/directives/complete-task.directive.spec.ts
@@ -0,0 +1,181 @@
+/*!
+ * @license
+ * Copyright 2019 Alfresco Software, Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Component, ViewChild, ContentChildren } from '@angular/core';
+import { CompleteTaskDirective } from './complete-task.directive';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { CoreModule, setupTestBed } from '@alfresco/adf-core';
+import { RouterTestingModule } from '@angular/router/testing';
+import { of } from 'rxjs';
+import { TaskCloudService } from '../task-header/services/task-cloud.service';
+import { taskCompleteCloudMock } from '../task-header/mocks/fake-complete-task.mock';
+
+describe('CompleteTaskDirective', () => {
+
+ @Component({
+ selector: 'adf-test-component',
+ template: ``
+ })
+ class TestComponent {
+
+ taskMock = 'test1234';
+ appNameMock = 'simple-app';
+
+ @ViewChild(CompleteTaskDirective)
+ completeTaskDirective: CompleteTaskDirective;
+
+ onCompleteTask(event: any) {
+ return event;
+ }
+ }
+
+ let fixture: ComponentFixture;
+ let taskCloudService: TaskCloudService;
+
+ setupTestBed({
+ imports: [
+ CoreModule.forRoot(),
+ RouterTestingModule
+ ],
+ declarations: [
+ TestComponent,
+ CompleteTaskDirective
+ ]
+ });
+
+ beforeEach(() => {
+ taskCloudService = TestBed.get(TaskCloudService);
+ fixture = TestBed.createComponent(TestComponent);
+ fixture.detectChanges();
+ });
+
+ it('should directive complete task', () => {
+ spyOn(taskCloudService, 'completeTask').and.returnValue(of(taskCompleteCloudMock));
+ const button = fixture.nativeElement.querySelector('button');
+ button.click();
+ expect(taskCloudService.completeTask).toHaveBeenCalled();
+ });
+});
+
+describe('Complete Task Directive validation errors', () => {
+
+ @Component({
+ selector: 'adf-no-fields-validation-component',
+ template: ''
+ })
+ class TestMissingInputDirectiveComponent {
+
+ appName = 'simple-app';
+ appNameUndefined = undefined;
+ appNameNull = null;
+
+ @ContentChildren(CompleteTaskDirective)
+ completeTaskValidationDirective: CompleteTaskDirective;
+
+ onCompleteTask(event: any) {
+ return event;
+ }
+ }
+
+ @Component({
+ selector: 'adf-no-taskid-validation-component',
+ template: ''
+ })
+ class TestMissingTaskIdDirectiveComponent {
+
+ appName = 'simple-app';
+
+ @ContentChildren(CompleteTaskDirective)
+ completeTaskValidationDirective: CompleteTaskDirective;
+
+ onCompleteTask(event: any) {
+ return event;
+ }
+ }
+
+ @Component({
+ selector: 'adf-undefined-appname-component',
+ template: ''
+ })
+ class TestInvalidAppNameUndefineddDirectiveComponent {
+
+ appName = 'simple-app';
+ taskMock = 'test1234';
+
+ @ContentChildren(CompleteTaskDirective)
+ completeTaskValidationDirective: CompleteTaskDirective;
+
+ onCompleteTask(event: any) {
+ return event;
+ }
+ }
+
+ @Component({
+ selector: 'adf-null-appname-component',
+ template: ''
+ })
+ class TestInvalidAppNameNulldDirectiveComponent {
+
+ appName = 'simple-app';
+ taskMock = 'test1234';
+
+ @ContentChildren(CompleteTaskDirective)
+ completeTaskValidationDirective: CompleteTaskDirective;
+
+ onCompleteTask(event: any) {
+ return event;
+ }
+ }
+
+ let fixture: ComponentFixture;
+
+ setupTestBed({
+ imports: [
+ CoreModule.forRoot(),
+ RouterTestingModule
+ ],
+ declarations: [
+ TestMissingTaskIdDirectiveComponent,
+ TestInvalidAppNameUndefineddDirectiveComponent,
+ TestInvalidAppNameNulldDirectiveComponent,
+ TestMissingInputDirectiveComponent,
+ CompleteTaskDirective
+ ]
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TestMissingInputDirectiveComponent);
+ });
+
+ it('should throw error when missing input', () => {
+ expect(() => fixture.detectChanges()).toThrowError();
+ });
+
+ it('should throw error when taskId is not set', () => {
+ fixture = TestBed.createComponent(TestMissingTaskIdDirectiveComponent);
+ expect( () => fixture.detectChanges()).toThrowError('Attribute taskId is required');
+ });
+
+ it('should throw error when appName is undefined', () => {
+ fixture = TestBed.createComponent(TestInvalidAppNameUndefineddDirectiveComponent);
+ expect( () => fixture.detectChanges()).toThrowError('Attribute appName is required');
+ });
+
+ it('should throw error when appName is null', () => {
+ fixture = TestBed.createComponent(TestInvalidAppNameUndefineddDirectiveComponent);
+ expect( () => fixture.detectChanges()).toThrowError('Attribute appName is required');
+ });
+});
diff --git a/lib/process-services-cloud/src/lib/task/directives/complete-task.directive.ts b/lib/process-services-cloud/src/lib/task/directives/complete-task.directive.ts
new file mode 100644
index 0000000000..7d604d5bc7
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/task/directives/complete-task.directive.ts
@@ -0,0 +1,82 @@
+/*!
+ * @license
+ * Copyright 2019 Alfresco Software, Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Directive, Input, HostListener, Output, EventEmitter, OnInit } from '@angular/core';
+import { TaskCloudService } from '../task-header/services/task-cloud.service';
+
+@Directive({
+ selector: '[adf-cloud-complete-task]'
+})
+export class CompleteTaskDirective implements OnInit {
+
+ /** (Required) The id of the task. */
+ @Input()
+ taskId: string;
+
+ /** (Required) The name of the application. */
+ @Input()
+ appName: string;
+
+ /** Emitted when the task is completed. */
+ @Output()
+ success: EventEmitter = new EventEmitter();
+
+ /** Emitted when the task cannot be completed. */
+ @Output()
+ error: EventEmitter = new EventEmitter();
+
+ invalidParams: string[] = [];
+
+ constructor(private taskListService: TaskCloudService) {}
+
+ ngOnInit() {
+ this.validateInputs();
+ }
+
+ validateInputs() {
+
+ if (!this.isTaskValid()) {
+ this.invalidParams.push('taskId');
+ }
+ if (!this.isAppValid()) {
+ this.invalidParams.push('appName');
+ }
+ if (this.invalidParams.length) {
+ throw new Error(`Attribute ${this.invalidParams.join(', ')} is required`);
+ }
+ }
+
+ isTaskValid() {
+ return this.taskId && this.taskId.length > 0;
+ }
+
+ isAppValid() {
+ return this.appName && this.appName.length > 0;
+ }
+
+ @HostListener('click')
+ async onClick() {
+ try {
+ const result = await this.taskListService.completeTask(this.appName, this.taskId).toPromise();
+ if (result) {
+ this.success.emit(result);
+ }
+ } catch (error) {
+ this.error.emit(error);
+ }
+
+ }
+}
diff --git a/lib/process-services-cloud/src/lib/task/directives/task-directive.module.ts b/lib/process-services-cloud/src/lib/task/directives/task-directive.module.ts
new file mode 100644
index 0000000000..8280bb9385
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/task/directives/task-directive.module.ts
@@ -0,0 +1,29 @@
+/*!
+ * @license
+ * Copyright 2019 Alfresco Software, Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { NgModule } from '@angular/core';
+import { CompleteTaskDirective } from './complete-task.directive';
+
+@NgModule({
+ declarations: [
+ CompleteTaskDirective
+ ],
+ exports: [
+ CompleteTaskDirective
+ ]
+})
+export class TaskDirectiveModule { }
diff --git a/lib/process-services-cloud/src/lib/task/public-api.ts b/lib/process-services-cloud/src/lib/task/public-api.ts
index d9950d290b..c2b1682a4e 100644
--- a/lib/process-services-cloud/src/lib/task/public-api.ts
+++ b/lib/process-services-cloud/src/lib/task/public-api.ts
@@ -21,3 +21,4 @@ export * from './start-task/public-api';
export * from './task-header/public-api';
export * from './task-cloud.module';
+export * from './directives/task-directive.module';
diff --git a/lib/process-services-cloud/src/lib/task/start-task/models/task-details-cloud.model.ts b/lib/process-services-cloud/src/lib/task/start-task/models/task-details-cloud.model.ts
index 64003220f1..2668626b93 100644
--- a/lib/process-services-cloud/src/lib/task/start-task/models/task-details-cloud.model.ts
+++ b/lib/process-services-cloud/src/lib/task/start-task/models/task-details-cloud.model.ts
@@ -36,7 +36,7 @@ export class TaskDetailsCloudModel {
priority: number;
processDefinitionId: string;
processInstanceId: string;
- status: string;
+ status: TaskStatusEnum;
standAlone: boolean;
candidateUsers: string[];
candidateGroups: string[];
@@ -73,8 +73,21 @@ export class TaskDetailsCloudModel {
this.memberOfCandidateUsers = obj.memberOfCandidateUsers || null;
}
}
+
+ isCompleted() {
+ return this.status && this.status === TaskStatusEnum.COMPLETED;
+ }
}
export interface StartTaskCloudResponseModel {
entry: TaskDetailsCloudModel;
}
+
+export enum TaskStatusEnum {
+ COMPLETED= 'COMPLETED',
+ DELETED = 'DELETED',
+ CREATED = 'CREATED',
+ ASSIGNED = 'ASSIGNED',
+ SUSPENDED = 'SUSPENDED',
+ CANCELLED = 'CANCELLED'
+}
diff --git a/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.html
index 0bd14053ad..a16a0935e7 100644
--- a/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.html
+++ b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.html
@@ -1,15 +1,20 @@
-
-
-
-
-
+{{ taskDetails.name }}
-
-
-
-
-
+
diff --git a/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.scss b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.scss
index c59023f992..6f440a3cdc 100644
--- a/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.scss
+++ b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.scss
@@ -22,11 +22,6 @@
&-claim-controls {
color: rgb(131, 131, 131);
}
-
- &-card-container {
- font-family: inherit;
- }
-
}
@media screen and ($mat-small) {
@@ -36,5 +31,4 @@
white-space: nowrap;
}
}
-
}
diff --git a/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.spec.ts
index f7ed7ae81a..acf648d631 100644
--- a/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.spec.ts
+++ b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.spec.ts
@@ -21,20 +21,22 @@ import { TaskHeaderCloudComponent } from './task-header-cloud.component';
import { taskDetailsCloudMock } from '../mocks/task-details-cloud.mock';
import { TaskHeaderCloudModule } from '../task-header-cloud.module';
import { By } from '@angular/platform-browser';
-import { TaskHeaderCloudService } from '../services/task-header-cloud.service';
import { of } from 'rxjs';
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
+import { RouterTestingModule } from '@angular/router/testing';
+import { TaskCloudService } from '../services/task-cloud.service';
-describe('TaskHeaderComponent', () => {
+describe('TaskHeaderCloudComponent', () => {
let component: TaskHeaderCloudComponent;
let fixture: ComponentFixture;
- let service: TaskHeaderCloudService;
+ let service: TaskCloudService;
let appConfigService: AppConfigService;
setupTestBed({
imports: [
ProcessServiceCloudTestingModule,
- TaskHeaderCloudModule
+ TaskHeaderCloudModule,
+ RouterTestingModule
]
});
@@ -43,7 +45,7 @@ describe('TaskHeaderComponent', () => {
component = fixture.componentInstance;
component.appName = 'myApp';
component.taskId = taskDetailsCloudMock.id;
- service = TestBed.get(TaskHeaderCloudService);
+ service = TestBed.get(TaskCloudService);
appConfigService = TestBed.get(AppConfigService);
spyOn(service, 'getTaskById').and.returnValue(of(taskDetailsCloudMock));
});
@@ -52,7 +54,7 @@ describe('TaskHeaderComponent', () => {
component.appName = undefined;
component.taskId = undefined;
fixture.detectChanges();
- expect(fixture.debugElement.children.length).toBe(0);
+ expect(fixture.debugElement.children.length).toBe(2);
}));
it('should display assignee', async(() => {
diff --git a/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.ts
index 812d3ca259..d53e3a8f00 100644
--- a/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.ts
+++ b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.ts
@@ -25,10 +25,10 @@ import {
AppConfigService,
UpdateNotification,
CardViewUpdateService,
- StorageService
+ IdentityUserService
} from '@alfresco/adf-core';
-import { TaskHeaderCloudService } from '../services/task-header-cloud.service';
import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model';
+import { TaskCloudService } from '../services/task-cloud.service';
@Component({
selector: 'adf-cloud-task-header',
@@ -64,11 +64,11 @@ export class TaskHeaderCloudComponent implements OnInit {
private currentUser: string;
constructor(
- private taskHeaderCloudService: TaskHeaderCloudService,
+ private taskCloudService: TaskCloudService,
private translationService: TranslationService,
private appConfig: AppConfigService,
private cardViewUpdateService: CardViewUpdateService,
- private storage: StorageService
+ private identityUserService: IdentityUserService
) { }
ngOnInit() {
@@ -79,12 +79,13 @@ export class TaskHeaderCloudComponent implements OnInit {
this.cardViewUpdateService.itemUpdated$.subscribe(this.updateTaskDetails.bind(this));
}
+
loadCurrentBpmUserId(): any {
- this.currentUser = this.storage.getItem('USERNAME');
+ this.currentUser = this.identityUserService.getCurrentUserInfo().username;
}
loadTaskDetailsById(appName: string, taskId: string): any {
- this.taskHeaderCloudService.getTaskById(appName, taskId).subscribe(
+ this.taskCloudService.getTaskById(appName, taskId).subscribe(
(taskDetails) => {
this.taskDetails = taskDetails;
if (this.taskDetails.parentTaskId) {
@@ -204,7 +205,7 @@ export class TaskHeaderCloudComponent implements OnInit {
* @param updateNotification
*/
private updateTaskDetails(updateNotification: UpdateNotification) {
- this.taskHeaderCloudService.updateTask(this.appName, this.taskId, updateNotification.changed)
+ this.taskCloudService.updateTask(this.appName, this.taskId, updateNotification.changed)
.subscribe(
(taskDetails) => {
this.taskDetails = taskDetails;
@@ -214,7 +215,7 @@ export class TaskHeaderCloudComponent implements OnInit {
}
private loadParentName(taskId) {
- this.taskHeaderCloudService.getTaskById(this.appName, taskId)
+ this.taskCloudService.getTaskById(this.appName, taskId)
.subscribe(
(taskDetails) => {
this.parentTaskName = taskDetails.name;
@@ -223,10 +224,6 @@ export class TaskHeaderCloudComponent implements OnInit {
);
}
- isCompleted() {
- return this.taskDetails && this.taskDetails.status === 'completed';
- }
-
isTaskClaimable(): boolean {
return !this.hasAssignee() && this.isCandidateMember();
}
@@ -240,7 +237,7 @@ export class TaskHeaderCloudComponent implements OnInit {
}
isTaskClaimedByCandidateMember(): boolean {
- return this.isCandidateMember() && this.isAssignedToCurrentUser() && !this.isCompleted();
+ return this.isCandidateMember() && this.isAssignedToCurrentUser() && !this.taskDetails.isCompleted();
}
isAssignedToCurrentUser(): boolean {
@@ -260,7 +257,7 @@ export class TaskHeaderCloudComponent implements OnInit {
}
claimTask() {
- this.taskHeaderCloudService.claimTask(this.appName, this.taskId, this.currentUser).subscribe(
+ this.taskCloudService.claimTask(this.appName, this.taskId, this.currentUser).subscribe(
(res: any) => {
this.loadTaskDetailsById(this.appName, this.taskId);
this.claim.emit(this.taskId);
@@ -268,7 +265,7 @@ export class TaskHeaderCloudComponent implements OnInit {
}
unclaimTask() {
- this.taskHeaderCloudService.unclaimTask(this.appName, this.taskId).subscribe(
+ this.taskCloudService.unclaimTask(this.appName, this.taskId).subscribe(
() => {
this.loadTaskDetailsById(this.appName, this.taskId);
this.unclaim.emit(this.taskId);
diff --git a/lib/process-services-cloud/src/lib/task/task-header/mocks/fake-complete-task.mock.ts b/lib/process-services-cloud/src/lib/task/task-header/mocks/fake-complete-task.mock.ts
new file mode 100644
index 0000000000..2ce3c42f31
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/task/task-header/mocks/fake-complete-task.mock.ts
@@ -0,0 +1,31 @@
+/*!
+ * @license
+ * Copyright 2019 Alfresco Software, Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export const taskCompleteCloudMock = {
+ 'entry': {
+ 'appName': 'simple-app',
+ 'appVersion': '',
+ 'serviceName': 'simple-app-rb',
+ 'serviceFullName': 'simple-app-rb',
+ 'serviceType': 'runtime-bundle',
+ 'serviceVersion': '',
+ 'id': '68d54a8f',
+ 'name': 'NXltAGmT',
+ 'priority': 0,
+ 'status': 'COMPLETED'
+ }
+};
diff --git a/lib/process-services-cloud/src/lib/task/task-header/public-api.ts b/lib/process-services-cloud/src/lib/task/task-header/public-api.ts
index 0be25b6c89..596020d265 100644
--- a/lib/process-services-cloud/src/lib/task/task-header/public-api.ts
+++ b/lib/process-services-cloud/src/lib/task/task-header/public-api.ts
@@ -17,4 +17,6 @@
export * from './components/task-header-cloud.component';
+export * from './services/task-cloud.service';
+
export * from './task-header-cloud.module';
diff --git a/lib/process-services-cloud/src/lib/task/task-header/services/task-header-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/task/task-header/services/task-cloud.service.spec.ts
similarity index 76%
rename from lib/process-services-cloud/src/lib/task/task-header/services/task-header-cloud.service.spec.ts
rename to lib/process-services-cloud/src/lib/task/task-header/services/task-cloud.service.spec.ts
index ec1bc12ae6..89b464d51d 100644
--- a/lib/process-services-cloud/src/lib/task/task-header/services/task-header-cloud.service.spec.ts
+++ b/lib/process-services-cloud/src/lib/task/task-header/services/task-cloud.service.spec.ts
@@ -15,16 +15,42 @@
* limitations under the License.
*/
-import { async } from '@angular/core/testing';
-import { setupTestBed } from '@alfresco/adf-core';
-import { AlfrescoApiServiceMock, LogService, AppConfigService, StorageService, CoreModule } from '@alfresco/adf-core';
+import { async, TestBed } from '@angular/core/testing';
+import { setupTestBed, IdentityUserService, IdentityUserModel, AlfrescoApiServiceMock } from '@alfresco/adf-core';
+import { LogService, AppConfigService, StorageService, CoreModule } from '@alfresco/adf-core';
+import { TaskCloudService } from './task-cloud.service';
+import { taskDetailsCloudMock } from '../mocks/task-details-cloud.mock';
+import { taskCompleteCloudMock } from '../mocks/fake-complete-task.mock';
import { fakeTaskDetailsCloud } from '../mocks/fake-task-details-response.mock';
-import { TaskHeaderCloudService } from './task-header-cloud.service';
-describe('Task Header Cloud Service', () => {
+describe('Task Cloud Service', () => {
- let service: TaskHeaderCloudService;
+ let service: TaskCloudService;
let alfrescoApiMock: AlfrescoApiServiceMock;
+ let identityService: IdentityUserService;
+ let identityUserWithOutFirstNameMock = { firstName: null, lastName: 'fake-identity-last-name', email: 'fakeIdentity@email.com', username: 'superadminuser' };
+ let getCurrentUserInfoStub;
+ let fakeIdentityUser: IdentityUserModel = new IdentityUserModel(identityUserWithOutFirstNameMock);
+
+ function returnFakeTaskCompleteResults() {
+ return {
+ oauth2Auth: {
+ callCustomApi : () => {
+ return Promise.resolve(taskCompleteCloudMock);
+ }
+ }
+ };
+ }
+
+ function returnFakeTaskCompleteResultsError() {
+ return {
+ oauth2Auth: {
+ callCustomApi : () => {
+ return Promise.reject(taskCompleteCloudMock);
+ }
+ }
+ };
+ }
function returnFakeTaskDetailsResults() {
return {
@@ -39,16 +65,53 @@ describe('Task Header Cloud Service', () => {
setupTestBed({
imports: [
CoreModule.forRoot()
- ]
+ ],
+ providers: [IdentityUserService, LogService]
});
beforeEach(async(() => {
+
+ identityService = TestBed.get(IdentityUserService);
+ getCurrentUserInfoStub = spyOn(identityService, 'getCurrentUserInfo');
+ getCurrentUserInfoStub.and.returnValue(fakeIdentityUser);
alfrescoApiMock = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService() );
- service = new TaskHeaderCloudService(alfrescoApiMock,
+ service = new TaskCloudService(alfrescoApiMock,
new AppConfigService(null),
- new LogService(new AppConfigService(null)));
+ new LogService(new AppConfigService(null)),
+ identityService);
+
}));
+ it('should complete a task', (done) => {
+ const appName = 'simple-app';
+ const taskId = '68d54a8f';
+ spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnFakeTaskCompleteResults);
+ service.completeTask(appName, taskId).subscribe((res: any) => {
+ expect(res).toBeDefined();
+ expect(res).not.toBeNull();
+ expect(res.entry.appName).toBe('simple-app');
+ expect(res.entry.id).toBe('68d54a8f');
+ done();
+ });
+ });
+
+ it('should not complete a task', (done) => {
+ spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnFakeTaskCompleteResultsError);
+ const appName = 'simple-app';
+ const taskId = '68d54a8f';
+
+ service.completeTask(appName, taskId).toPromise().then( (res: any) => {
+ }, (error) => {
+ expect(error).toBeDefined();
+ done();
+ });
+ });
+
+ it('should canCompleteTask', () => {
+ const canCompleteTaskResult = service.canCompleteTask(taskDetailsCloudMock);
+ expect(canCompleteTaskResult).toBe(true);
+ });
+
it('should return the task details when querying by id', (done) => {
const appName = 'taskp-app';
const taskId = '68d54a8f';
diff --git a/lib/process-services-cloud/src/lib/task/task-header/services/task-header-cloud.service.ts b/lib/process-services-cloud/src/lib/task/task-header/services/task-cloud.service.ts
similarity index 71%
rename from lib/process-services-cloud/src/lib/task/task-header/services/task-header-cloud.service.ts
rename to lib/process-services-cloud/src/lib/task/task-header/services/task-cloud.service.ts
index 1f1a98b5d7..1c8bb1c25b 100644
--- a/lib/process-services-cloud/src/lib/task/task-header/services/task-header-cloud.service.ts
+++ b/lib/process-services-cloud/src/lib/task/task-header/services/task-cloud.service.ts
@@ -15,27 +15,69 @@
* limitations under the License.
*/
-import { AlfrescoApiService, LogService, AppConfigService } from '@alfresco/adf-core';
import { Injectable } from '@angular/core';
-import { Observable, from, throwError } from 'rxjs';
+import { AlfrescoApiService, LogService, AppConfigService, IdentityUserService } from '@alfresco/adf-core';
+import { from, throwError, Observable } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model';
@Injectable({
providedIn: 'root'
})
-export class TaskHeaderCloudService {
+export class TaskCloudService {
+
contextRoot: string;
contentTypes = ['application/json'];
accepts = ['application/json'];
returnType = Object;
- constructor(private alfrescoApiService: AlfrescoApiService,
- private appConfigService: AppConfigService,
- private logService: LogService) {
+ constructor(
+ private apiService: AlfrescoApiService,
+ private appConfigService: AppConfigService,
+ private logService: LogService,
+ private identityUserService: IdentityUserService
+ ) {
this.contextRoot = this.appConfigService.get('bpmHost', '');
}
+ /**
+ * Complete a task.
+ * @param appName Name of the app
+ * @param taskId ID of the task to complete
+ * @returns Details of the task that was completed
+ */
+ completeTask(appName: string, taskId: string) {
+ const queryUrl = this.buildCompleteTaskUrl(appName, taskId);
+ const bodyParam = { 'payloadType': 'CompleteTaskPayload' };
+ const pathParams = {}, queryParams = {}, headerParams = {},
+ formParams = {}, contentTypes = ['application/json'], accepts = ['application/json'];
+
+ return from(
+ this.apiService
+ .getInstance()
+ .oauth2Auth.callCustomApi(
+ queryUrl, 'POST', pathParams, queryParams,
+ headerParams, formParams, bodyParam,
+ contentTypes, accepts, null, null)
+ ).pipe(
+ catchError((err) => this.handleError(err))
+ );
+ }
+
+ /**
+ * Validate if a task can be completed.
+ * @param taskDetails task details object
+ * @returns Boolean value if the task can be completed
+ */
+ canCompleteTask(taskDetails: TaskDetailsCloudModel): boolean {
+ const currentUser = this.identityUserService.getCurrentUserInfo().username;
+ return taskDetails.owner === currentUser && !taskDetails.isCompleted();
+ }
+
+ private buildCompleteTaskUrl(appName: string, taskId: string): any {
+ return `${this.appConfigService.get('bpmHost')}/${appName}-rb/v1/tasks/${taskId}/complete`;
+ }
+
/**
* Gets details of a task.
* @param appName Name of the app
@@ -46,7 +88,7 @@ export class TaskHeaderCloudService {
if (appName && taskId) {
let queryUrl = `${this.contextRoot}/${appName}-query/v1/tasks/${taskId}`;
- return from(this.alfrescoApiService.getInstance()
+ return from(this.apiService.getInstance()
.oauth2Auth.callCustomApi(queryUrl, 'GET',
null, null, null,
null, null,
@@ -77,7 +119,7 @@ export class TaskHeaderCloudService {
updatePayload.payloadType = 'UpdateTaskPayload';
let queryUrl = `${this.contextRoot}/${appName}-rb/v1/tasks/${taskId}`;
- return from(this.alfrescoApiService.getInstance()
+ return from(this.apiService.getInstance()
.oauth2Auth.callCustomApi(queryUrl, 'PUT',
null, null, null,
null, updatePayload,
@@ -106,7 +148,7 @@ export class TaskHeaderCloudService {
if (appName && taskId) {
let queryUrl = `${this.contextRoot}/${appName}-rb/v1/tasks/${taskId}/claim?assignee=${assignee}`;
- return from(this.alfrescoApiService.getInstance()
+ return from(this.apiService.getInstance()
.oauth2Auth.callCustomApi(queryUrl, 'POST',
null, null, null,
null, null,
@@ -134,7 +176,7 @@ export class TaskHeaderCloudService {
if (appName && taskId) {
let queryUrl = `${this.contextRoot}/${appName}-rb/v1/tasks/${taskId}/release`;
- return from(this.alfrescoApiService.getInstance()
+ return from(this.apiService.getInstance()
.oauth2Auth.callCustomApi(queryUrl, 'POST',
null, null, null,
null, null,
diff --git a/lib/process-services-cloud/src/lib/task/task-header/task-header-cloud.module.ts b/lib/process-services-cloud/src/lib/task/task-header/task-header-cloud.module.ts
index 6571c48891..f676d0bfd5 100644
--- a/lib/process-services-cloud/src/lib/task/task-header/task-header-cloud.module.ts
+++ b/lib/process-services-cloud/src/lib/task/task-header/task-header-cloud.module.ts
@@ -20,7 +20,6 @@ import { CommonModule } from '@angular/common';
import { MaterialModule } from '../../material.module';
import { DataTableModule, TemplateModule, CardViewModule, CoreModule } from '@alfresco/adf-core';
import { TaskHeaderCloudComponent } from './components/task-header-cloud.component';
-import { TaskHeaderCloudService } from './services/task-header-cloud.service';
@NgModule({
imports: [
@@ -36,9 +35,6 @@ import { TaskHeaderCloudService } from './services/task-header-cloud.service';
],
exports: [
TaskHeaderCloudComponent
- ],
- providers: [
- TaskHeaderCloudService
]
})
export class TaskHeaderCloudModule { }