From 133a5c3df6c957e704fcb5484b2f4a8d49789db5 Mon Sep 17 00:00:00 2001
From: arditdomi <32884230+arditdomi@users.noreply.github.com>
Date: Tue, 31 Dec 2019 15:41:54 +0000
Subject: [PATCH] [AAE-1137] Refactor delete process directive (#5317)
* [AAE-1137] Refactor delete process directive
* [AAE-1137] Fix unit tests
* [AAE-1137] Add can show directive output
* [AAE-1137] Remove from demo-shell, update documentation
* [AAE-1137] Refactor cancel process directive
* [AAE-1137] documentation typo
* [AAE-1137] Add unsubscription
* [AAE-1137] Fix build errors
* [AAE-1137] Remove unused declaration
---
.../process-details-cloud-demo.component.html | 2 -
.../process-details-cloud-demo.component.scss | 1 +
.../directives/cancel-process.directive.md | 23 +++
.../cancel-process.directive.spec.ts | 84 ++++++++++
.../directives/cancel-process.directive.ts | 94 +++++++++++
.../delete-process.directive.spec.ts | 149 ------------------
.../directives/delete-process.directive.ts | 108 -------------
.../directives/process-directive.module.ts | 6 +-
.../src/lib/process/directives/public-api.ts | 2 +-
.../lib/process/mock/process-details.mock.ts | 22 +++
10 files changed, 228 insertions(+), 263 deletions(-)
create mode 100644 docs/process-services-cloud/directives/cancel-process.directive.md
create mode 100644 lib/process-services-cloud/src/lib/process/directives/cancel-process.directive.spec.ts
create mode 100644 lib/process-services-cloud/src/lib/process/directives/cancel-process.directive.ts
delete mode 100644 lib/process-services-cloud/src/lib/process/directives/delete-process.directive.spec.ts
delete mode 100644 lib/process-services-cloud/src/lib/process/directives/delete-process.directive.ts
create mode 100644 lib/process-services-cloud/src/lib/process/mock/process-details.mock.ts
diff --git a/demo-shell/src/app/components/cloud/process-details-cloud-demo.component.html b/demo-shell/src/app/components/cloud/process-details-cloud-demo.component.html
index e777e9aa04..a40f50d9f1 100644
--- a/demo-shell/src/app/components/cloud/process-details-cloud-demo.component.html
+++ b/demo-shell/src/app/components/cloud/process-details-cloud-demo.component.html
@@ -1,4 +1,3 @@
-
@@ -22,4 +21,3 @@
[processInstanceId]="processInstanceId">
-
diff --git a/demo-shell/src/app/components/cloud/process-details-cloud-demo.component.scss b/demo-shell/src/app/components/cloud/process-details-cloud-demo.component.scss
index 91e2fbe20d..c65339b039 100644
--- a/demo-shell/src/app/components/cloud/process-details-cloud-demo.component.scss
+++ b/demo-shell/src/app/components/cloud/process-details-cloud-demo.component.scss
@@ -11,4 +11,5 @@
margin-left: 10px;
width: 25%;
}
+
}
diff --git a/docs/process-services-cloud/directives/cancel-process.directive.md b/docs/process-services-cloud/directives/cancel-process.directive.md
new file mode 100644
index 0000000000..3c9bc6735c
--- /dev/null
+++ b/docs/process-services-cloud/directives/cancel-process.directive.md
@@ -0,0 +1,23 @@
+---
+Title: Cancel Process Directive
+Added: v3.7.0
+Status: Experimental
+Last reviewed: 2019-12-09
+---
+
+# [Cancel process directive](../../../lib/process-services-cloud/src/lib/process/directives/cancel-process.directive.ts "Defined in cancel-process.directive.ts")
+
+Cancels a process
+
+## Basic Usage
+
+```html
+
+```
+
+### Events
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| error | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`` | Emitted when the process can not be cancelled. |
+| success | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`` | Emitted when the process is cancelled. |
diff --git a/lib/process-services-cloud/src/lib/process/directives/cancel-process.directive.spec.ts b/lib/process-services-cloud/src/lib/process/directives/cancel-process.directive.spec.ts
new file mode 100644
index 0000000000..1862a8d46c
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/process/directives/cancel-process.directive.spec.ts
@@ -0,0 +1,84 @@
+/*!
+ * @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 } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { CoreModule, IdentityUserService, setupTestBed } from '@alfresco/adf-core';
+import { CancelProcessDirective } from './cancel-process.directive';
+import { processDetailsMockRunning, processDetailsMockCompleted } from '../mock/process-details.mock';
+
+describe('CancelProcessDirective', () => {
+
+ @Component({
+ selector: 'adf-cloud-cancel-process-test-component',
+ template: ''
+ })
+ class TestComponent {
+
+ @ViewChild(CancelProcessDirective)
+ cancelProcessDirective: CancelProcessDirective;
+ }
+
+ let fixture: ComponentFixture;
+ let identityUserService: IdentityUserService;
+ let component: TestComponent;
+
+ setupTestBed({
+ imports: [
+ CoreModule.forRoot()
+ ],
+ declarations: [
+ TestComponent,
+ CancelProcessDirective
+ ]
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TestComponent);
+ component = fixture.componentInstance;
+ identityUserService = TestBed.get(IdentityUserService);
+ spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue({username: 'usermock'});
+ fixture.detectChanges();
+ });
+
+ it('should directive call cancelProcess when button is clicked', () => {
+ const cancelProcessSpy = spyOn(component.cancelProcessDirective, 'cancelProcess').and.callThrough();
+ const button = fixture.nativeElement.querySelector('button');
+ button.click();
+ expect(cancelProcessSpy).toHaveBeenCalled();
+ });
+
+ it('should checkCanCancelProcess return false when process status is COMPLETED', () => {
+ component.cancelProcessDirective.processInstanceDetails = processDetailsMockCompleted;
+ fixture.detectChanges();
+ expect(component.cancelProcessDirective.checkCanCancelProcess()).toBeFalsy();
+ });
+
+ it('should checkCanCancelProcess return true when process status is RUNNING and logged in user is the processInitiator', () => {
+ component.cancelProcessDirective.processInstanceDetails = processDetailsMockRunning;
+ fixture.detectChanges();
+ expect(component.cancelProcessDirective.checkCanCancelProcess()).toBeTruthy();
+ });
+
+ it('should checkCanCancelProcess return false when logged in user is not the processInitiator', () => {
+ component.cancelProcessDirective.processInstanceDetails = processDetailsMockRunning;
+ component.cancelProcessDirective.processInstanceDetails.initiator = 'mock-user';
+ fixture.detectChanges();
+ expect(component.cancelProcessDirective.checkCanCancelProcess()).toBeFalsy();
+ });
+
+});
diff --git a/lib/process-services-cloud/src/lib/process/directives/cancel-process.directive.ts b/lib/process-services-cloud/src/lib/process/directives/cancel-process.directive.ts
new file mode 100644
index 0000000000..f25e418808
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/process/directives/cancel-process.directive.ts
@@ -0,0 +1,94 @@
+/*!
+ * @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, HostListener, Output, EventEmitter, OnInit, OnDestroy, ElementRef } from '@angular/core';
+import { IdentityUserService } from '@alfresco/adf-core';
+import { ProcessCloudService } from '../services/process-cloud.service';
+import { takeUntil } from 'rxjs/operators';
+import { Subject } from 'rxjs';
+import { ProcessInstanceCloud } from '../start-process/models/process-instance-cloud.model';
+
+@Directive({
+ selector: '[adf-cloud-cancel-process]'
+})
+export class CancelProcessDirective implements OnInit, OnDestroy {
+
+ /** Emitted when the process is cancelled. */
+ @Output()
+ success: EventEmitter = new EventEmitter();
+
+ /** Emitted when the process cannot be cancelled. */
+ @Output()
+ error: EventEmitter = new EventEmitter();
+
+ processInstanceDetails: ProcessInstanceCloud;
+
+ canCancelProcess = false;
+
+ private onDestroy$ = new Subject();
+
+ constructor(
+ private elementRef: ElementRef,
+ private processCloudService: ProcessCloudService,
+ private identityUserService: IdentityUserService) {}
+
+ ngOnInit() {
+ this.processCloudService.dataChangesDetected
+ .pipe(takeUntil(this.onDestroy$))
+ .subscribe((processDetails: ProcessInstanceCloud) => {
+ this.processInstanceDetails = processDetails;
+ this.canCancelProcess = this.checkCanCancelProcess();
+ this.setElementVisibility();
+ });
+ }
+
+ @HostListener('click')
+ async onClick() {
+ try {
+ this.cancelProcess();
+ } catch (error) {
+ this.error.emit(error);
+ }
+ }
+
+ private setElementVisibility() {
+ this.elementRef.nativeElement.disabled = !this.canCancelProcess;
+ }
+
+ checkCanCancelProcess(): boolean {
+ const currentUser = this.identityUserService.getCurrentUserInfo().username;
+ return this.processInstanceDetails.initiator === currentUser && this.processInstanceDetails.status === 'RUNNING';
+ }
+
+ async cancelProcess() {
+ if (this.canCancelProcess) {
+ await this.processCloudService.cancelProcess(this.processInstanceDetails.appName, this.processInstanceDetails.id)
+ .pipe(takeUntil(this.onDestroy$))
+ .subscribe((response) => {
+ this.success.emit(response);
+ }, ((error) => {
+ this.error.emit(error);
+ }));
+ } else {
+ this.error.emit('Permission denied, only process initiator can cancel the process');
+ }
+ }
+
+ ngOnDestroy() {
+ this.onDestroy$.next(true);
+ this.onDestroy$.complete();
+ }
+}
diff --git a/lib/process-services-cloud/src/lib/process/directives/delete-process.directive.spec.ts b/lib/process-services-cloud/src/lib/process/directives/delete-process.directive.spec.ts
deleted file mode 100644
index 38a0bc88fe..0000000000
--- a/lib/process-services-cloud/src/lib/process/directives/delete-process.directive.spec.ts
+++ /dev/null
@@ -1,149 +0,0 @@
-/*!
- * @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 } from '@angular/core';
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { CoreModule, IdentityUserService, setupTestBed } from '@alfresco/adf-core';
-import { DeleteProcessDirective } from './delete-process.directive';
-import { ProcessCloudService } from '../services/process-cloud.service';
-import { of } from 'rxjs';
-
-describe('DeleteProcessDirective', () => {
-
- @Component({
- selector: 'adf-cloud-delete-process-test-component',
- template: ''
- })
- class TestComponent {
-
- processIdMock = 'process-id-mock';
- appNameMock = 'app-mock';
- initiatorMock = 'user-mock';
-
- @ViewChild(DeleteProcessDirective)
- deleteProcessDirective: DeleteProcessDirective;
- }
-
- let fixture: ComponentFixture;
- let processCloudService: ProcessCloudService;
- let identityUserService: IdentityUserService;
-
- setupTestBed({
- imports: [
- CoreModule.forRoot()
- ],
- declarations: [
- TestComponent,
- DeleteProcessDirective
- ]
- });
-
- beforeEach(() => {
- processCloudService = TestBed.get(ProcessCloudService);
- fixture = TestBed.createComponent(TestComponent);
- identityUserService = TestBed.get(IdentityUserService);
- spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue({username: 'user-mock'});
- fixture.detectChanges();
- });
-
- it('should call delete process service when click', () => {
- spyOn(processCloudService, 'cancelProcess').and.returnValue(of({}));
- const button = fixture.nativeElement.querySelector('button');
- button.click();
- expect(processCloudService.cancelProcess).toHaveBeenCalled();
- });
-
-});
-
-describe('Validation Errors', () => {
- let fixture;
-
- @Component({
- template: ''
- })
- class DeleteProcessMissingInputsDirectiveComponent {
- @ViewChild(DeleteProcessDirective)
- deleteProcessDirective: DeleteProcessDirective;
- }
-
- @Component({
- template: ''
- })
- class DeleteProcessMissingAppNameDirectiveComponent {
- @ViewChild(DeleteProcessDirective)
- deleteProcessDirective: DeleteProcessDirective;
-
- processId = 'id-mock';
- processInitiator = 'user-mock';
- }
-
- @Component({
- template: ''
- })
- class DeleteProcessMissingProcessIdDirectiveComponent {
- @ViewChild(DeleteProcessDirective)
- deleteProcessDirective: DeleteProcessDirective;
-
- appName = 'app-mock';
- processInitiator = 'user-mock';
- }
-
- @Component({
- template: ''
- })
- class DeleteProcessMissingProcessInitiatorDirectiveComponent {
- @ViewChild(DeleteProcessDirective)
- deleteProcessDirective: DeleteProcessDirective;
-
- appName = 'app-mock';
- processId = 'id-mock';
- }
-
- setupTestBed({
- imports: [
- CoreModule.forRoot()
- ],
- declarations: [
- DeleteProcessMissingInputsDirectiveComponent,
- DeleteProcessMissingAppNameDirectiveComponent,
- DeleteProcessMissingProcessIdDirectiveComponent,
- DeleteProcessMissingProcessInitiatorDirectiveComponent,
- DeleteProcessDirective
- ]
- });
-
- it('should throw an error when appName processId and processInitiator are undefined', () => {
- fixture = TestBed.createComponent(DeleteProcessMissingInputsDirectiveComponent);
- expect(() => fixture.detectChanges()).toThrowError('Attribute processId, appName, processInitiator is required');
- });
-
- it('should throw an error when appName is missing', () => {
- fixture = TestBed.createComponent(DeleteProcessMissingAppNameDirectiveComponent);
- expect(() => fixture.detectChanges()).toThrowError('Attribute appName is required');
- });
-
- it('should throw an error when processId is missing', () => {
- fixture = TestBed.createComponent(DeleteProcessMissingProcessIdDirectiveComponent);
- expect(() => fixture.detectChanges()).toThrowError('Attribute processId is required');
- });
-
- it('should throw an error when processInitiator is missing', () => {
- fixture = TestBed.createComponent(DeleteProcessMissingProcessInitiatorDirectiveComponent);
- expect(() => fixture.detectChanges()).toThrowError('Attribute processInitiator is required');
- });
-
-});
diff --git a/lib/process-services-cloud/src/lib/process/directives/delete-process.directive.ts b/lib/process-services-cloud/src/lib/process/directives/delete-process.directive.ts
deleted file mode 100644
index 4f86108124..0000000000
--- a/lib/process-services-cloud/src/lib/process/directives/delete-process.directive.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-/*!
- * @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 { IdentityUserService } from '@alfresco/adf-core';
-import { ProcessCloudService } from '../services/process-cloud.service';
-
-@Directive({
- // tslint:disable-next-line: directive-selector
- selector: '[adf-cloud-delete-process]'
-})
-export class DeleteProcessDirective implements OnInit {
-
- /** (Required) The id of the process. */
- @Input()
- processId: string;
-
- /** (Required) The name of the application. */
- @Input()
- appName: string;
-
- /** (Required) The process initiator */
- @Input()
- processInitiator: string;
-
- /** Emitted when the process is deleted. */
- @Output()
- success: EventEmitter = new EventEmitter();
-
- /** Emitted when the process cannot be deleted. */
- @Output()
- error: EventEmitter = new EventEmitter();
-
- invalidParams: string[] = [];
-
- constructor(
- private processCloudService: ProcessCloudService,
- private identityUserService: IdentityUserService) {}
-
- ngOnInit() {
- this.validateInputs();
- }
-
- validateInputs() {
-
- if (!this.isProcessValid()) {
- this.invalidParams.push('processId');
- }
- if (!this.isAppValid()) {
- this.invalidParams.push('appName');
- }
- if (!this.isProcessInitiatorValid()) {
- this.invalidParams.push('processInitiator');
- }
- if (this.invalidParams.length) {
- throw new Error(`Attribute ${this.invalidParams.join(', ')} is required`);
- }
- }
-
- isProcessValid(): boolean {
- return this.processId && this.processId.length > 0;
- }
-
- isAppValid(): boolean {
- return (this.appName && this.appName.length > 0);
- }
-
- isProcessInitiatorValid(): boolean {
- return (this.processInitiator && this.processInitiator.length > 0);
- }
-
- @HostListener('click')
- async onClick() {
- try {
- this.deleteProcess();
- } catch (error) {
- this.error.emit(error);
- }
- }
-
- private async deleteProcess() {
- const currentUser: string = this.identityUserService.getCurrentUserInfo().username;
- if (currentUser === this.processInitiator) {
- await this.processCloudService.cancelProcess(this.appName, this.processId)
- .subscribe((response) => {
- this.success.emit(response);
- }, ((error) => {
- this.error.emit(error);
- }));
- } else {
- this.error.emit('Permission denied');
- }
-
- }
-}
diff --git a/lib/process-services-cloud/src/lib/process/directives/process-directive.module.ts b/lib/process-services-cloud/src/lib/process/directives/process-directive.module.ts
index d544102f15..cf094bce36 100644
--- a/lib/process-services-cloud/src/lib/process/directives/process-directive.module.ts
+++ b/lib/process-services-cloud/src/lib/process/directives/process-directive.module.ts
@@ -16,14 +16,14 @@
*/
import { NgModule } from '@angular/core';
-import { DeleteProcessDirective } from './delete-process.directive';
+import { CancelProcessDirective } from './cancel-process.directive';
@NgModule({
declarations: [
- DeleteProcessDirective
+ CancelProcessDirective
],
exports: [
- DeleteProcessDirective
+ CancelProcessDirective
]
})
export class ProcessDirectiveModule { }
diff --git a/lib/process-services-cloud/src/lib/process/directives/public-api.ts b/lib/process-services-cloud/src/lib/process/directives/public-api.ts
index 8919c86169..505e0c9b7f 100644
--- a/lib/process-services-cloud/src/lib/process/directives/public-api.ts
+++ b/lib/process-services-cloud/src/lib/process/directives/public-api.ts
@@ -15,6 +15,6 @@
* limitations under the License.
*/
-export * from './delete-process.directive';
+export * from './cancel-process.directive';
export * from './process-directive.module';
diff --git a/lib/process-services-cloud/src/lib/process/mock/process-details.mock.ts b/lib/process-services-cloud/src/lib/process/mock/process-details.mock.ts
new file mode 100644
index 0000000000..f64d130c0a
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/process/mock/process-details.mock.ts
@@ -0,0 +1,22 @@
+/*!
+ * @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 { ProcessInstanceCloud } from '../start-process/models/process-instance-cloud.model';
+
+export let processDetailsMockRunning = new ProcessInstanceCloud({ initiator: 'usermock', status: 'RUNNING' });
+
+export let processDetailsMockCompleted = new ProcessInstanceCloud({ initiator: 'usermock', status: 'COMPLETED' });