From 571f147dc8bb525329301ccb5ccf7386745d2416 Mon Sep 17 00:00:00 2001 From: Pablo Martinez Garcia Date: Mon, 26 Aug 2024 11:08:04 +0200 Subject: [PATCH 1/4] AAE-25190 Restrieve start process cloud customisation --- .../start-process-cloud.component.html | 7 +- .../start-process-cloud.component.spec.ts | 80 +++++++++++++++++++ .../start-process-cloud.component.ts | 76 +++++++++++++----- .../start-process-cloud.service.spec.ts | 25 ++++++ .../services/start-process-cloud.service.ts | 22 +++++ 5 files changed, 189 insertions(+), 21 deletions(-) diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html index 154da753f25..97bd53ebf36 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html +++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html @@ -100,14 +100,15 @@
diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts index 1866049f3a8..37422956b1f 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts @@ -67,6 +67,7 @@ describe('StartProcessCloudComponent', () => { let startProcessWithFormSpy: jasmine.Spy; let formDefinitionSpy: jasmine.Spy; let getStartEventFormStaticValuesMappingSpy: jasmine.Spy; + let getStartEventConstantSpy: jasmine.Spy; const firstChange = new SimpleChange(undefined, 'myApp', true); @@ -112,6 +113,7 @@ describe('StartProcessCloudComponent', () => { startProcessSpy = spyOn(processService, 'startProcess').and.returnValue(of(fakeProcessInstance)); startProcessWithFormSpy = spyOn(processService, 'startProcessWithForm').and.returnValue(of(fakeProcessWithFormInstance)); getStartEventFormStaticValuesMappingSpy = spyOn(processService, 'getStartEventFormStaticValuesMapping').and.returnValue(of([])); + getStartEventConstantSpy = spyOn(processService, 'getStartEventConstants').and.returnValue(of([])); loader = TestbedHarnessEnvironment.loader(fixture); }); @@ -210,6 +212,84 @@ describe('StartProcessCloudComponent', () => { tick(550); expect(component.resolvedValues).toEqual(staticInputs.concat(values)); })); + + describe('start event constants', () => { + it('should not display the buttons when they are disabled by the constants', fakeAsync(() => { + const constants: TaskVariableCloud[] = [ + new TaskVariableCloud({ name: 'startEnabled', value: 'false' }), + new TaskVariableCloud({ name: 'cancelEnabled', value: 'false' }) + ]; + getStartEventConstantSpy.and.returnValue(of(constants)); + + component.name = 'My new process'; + component.processDefinitionName = 'processwithoutform2'; + getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinitionWithoutForm(component.processDefinitionName))); + + const change = new SimpleChange(null, 'MyApp', true); + component.ngOnChanges({ appName: change }); + fixture.detectChanges(); + tick(550); + + fixture.detectChanges(); + + const startButton = fixture.nativeElement.querySelector('#button-start'); + const cancelButton = fixture.nativeElement.querySelector('#cancel_process'); + + expect(startButton).toBeNull(); + expect(cancelButton).toBeNull(); + })); + + it('should display the customised button labels when they are set in the constants', fakeAsync(() => { + const constants: TaskVariableCloud[] = [ + new TaskVariableCloud({ name: 'startEnabled', value: 'true' }), + new TaskVariableCloud({ name: 'startLabel', value: 'Start' }), + new TaskVariableCloud({ name: 'cancelEnabled', value: 'true' }), + new TaskVariableCloud({ name: 'cancelLabel', value: 'Cancel' }) + ]; + getStartEventConstantSpy.and.returnValue(of(constants)); + + component.name = 'My new process'; + component.processDefinitionName = 'processwithoutform2'; + getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinitionWithoutForm(component.processDefinitionName))); + fixture.detectChanges(); + + const change = new SimpleChange(null, 'MyApp', true); + component.ngOnChanges({ appName: change }); + fixture.detectChanges(); + tick(550); + + fixture.detectChanges(); + + const startButton = fixture.nativeElement.querySelector('#button-start'); + const cancelButton = fixture.nativeElement.querySelector('#cancel_process'); + + expect(startButton.textContent?.trim()).toEqual('Start'); + expect(cancelButton.textContent?.trim()).toEqual('Cancel'); + })); + + it('should load with default values when retrieving the constants fails', fakeAsync(() => { + getStartEventConstantSpy.and.returnValue(throwError(() => new Error('test'))); + component.name = 'My new process'; + component.processDefinitionName = 'processwithoutform2'; + getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinitionWithoutForm(component.processDefinitionName))); + fixture.detectChanges(); + + const change = new SimpleChange(null, 'MyApp', true); + component.ngOnChanges({ appName: change }); + fixture.detectChanges(); + tick(550); + + fixture.detectChanges(); + + const startButton = fixture.nativeElement.querySelector('#button-start'); + const cancelButton = fixture.nativeElement.querySelector('#cancel_process'); + + expect(startButton).not.toBeNull(); + expect(cancelButton).not.toBeNull(); + expect(startButton.textContent?.trim()).toEqual(component.defaultStartProcessButtonLabel); + expect(cancelButton.textContent?.trim()).toEqual(component.defaultCancelProcessButtonLabel); + })); + }); }); describe('start a process with start form', () => { diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts index 229833a5ef9..750393f8792 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts @@ -30,18 +30,18 @@ import { ViewEncapsulation } from '@angular/core'; -import { ContentLinkModel, FORM_FIELD_VALIDATORS, FormFieldValidator, FormModel } from '@alfresco/adf-core'; +import { ContentLinkModel, FORM_FIELD_VALIDATORS, FormFieldValidator, FormModel, TranslationService } from '@alfresco/adf-core'; import { AbstractControl, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms'; import { MatAutocompleteTrigger } from '@angular/material/autocomplete'; -import { Subject } from 'rxjs'; -import { debounceTime, takeUntil } from 'rxjs/operators'; -import { TaskVariableCloud } from '../../../form/models/task-variable-cloud.model'; -import { ProcessDefinitionCloud } from '../../../models/process-definition-cloud.model'; -import { ProcessNameCloudPipe } from '../../../pipes/process-name-cloud.pipe'; +import { catchError, debounceTime, takeUntil } from 'rxjs/operators'; import { ProcessInstanceCloud } from '../models/process-instance-cloud.model'; import { ProcessPayloadCloud } from '../models/process-payload-cloud.model'; import { ProcessWithFormPayloadCloud } from '../models/process-with-form-payload-cloud.model'; import { StartProcessCloudService } from '../services/start-process-cloud.service'; +import { forkJoin, of, Subject } from 'rxjs'; +import { ProcessDefinitionCloud } from '../../../models/process-definition-cloud.model'; +import { TaskVariableCloud } from '../../../form/models/task-variable-cloud.model'; +import { ProcessNameCloudPipe } from '../../../pipes/process-name-cloud.pipe'; const MAX_NAME_LENGTH: number = 255; const PROCESS_DEFINITION_DEBOUNCE: number = 300; @@ -131,6 +131,11 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy isFormCloudLoading = false; processDefinitionLoaded = false; + displayStartProcessButton = true; + displayCancelButton = true; + startProcessButtonLabel: string; + cancelButtonLabel: string; + formCloud?: FormModel; processForm = new FormGroup({ processInstanceName: new FormControl('', [ @@ -172,6 +177,21 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy return !!this.processDefinitionCurrent?.formKey; } + get defaultStartProcessButtonLabel(): string { + return this.translateService.instant('ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.ACTION.START').toUpperCase(); + } + + get defaultCancelProcessButtonLabel(): string { + return this.translateService.instant('ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.ACTION.CANCEL').toUpperCase(); + } + + constructor(private translateService: TranslationService) { + this.startProcessButtonLabel = this.translateService + .instant('ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.ACTION.START') + .toUpperCase(); + this.cancelButtonLabel = this.translateService.instant('ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.ACTION.CANCEL').toUpperCase(); + } + ngOnInit() { this.initFieldValidators(); @@ -230,19 +250,39 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy (process: ProcessDefinitionCloud) => process.name === selectedProcessDefinitionName || process.key === selectedProcessDefinitionName ); - this.startProcessCloudService.getStartEventFormStaticValuesMapping(this.appName, processDefinitionCurrent.id).subscribe( - (staticMappings) => { - this.staticMappings = staticMappings; - this.resolvedValues = this.staticMappings.concat(this.values || []); - this.processDefinitionCurrent = processDefinitionCurrent; - this.isFormCloudLoading = false; - }, - () => { - this.resolvedValues = this.values; - this.processDefinitionCurrent = processDefinitionCurrent; - this.isFormCloudLoading = false; + forkJoin([ + this.startProcessCloudService + .getStartEventFormStaticValuesMapping(this.appName, processDefinitionCurrent.id) + .pipe(catchError(() => of([] as TaskVariableCloud[]))), + this.startProcessCloudService + .getStartEventConstants(this.appName, processDefinitionCurrent.id) + .pipe(catchError(() => of([] as TaskVariableCloud[]))) + ]).subscribe(([staticMappings, constants]) => { + this.staticMappings = staticMappings; + this.resolvedValues = this.staticMappings.concat(this.values || []); + this.processDefinitionCurrent = processDefinitionCurrent; + this.isFormCloudLoading = false; + + const displayStart = constants?.find((constant) => constant.name === 'startEnabled'); + const startLabel = constants?.find((constant) => constant.name === 'startLabel'); + + const displayCancel = constants?.find((constant) => constant.name === 'cancelEnabled'); + const cancelLabel = constants?.find((constant) => constant.name === 'cancelLabel'); + + if (displayStart) { + this.displayStartProcessButton = displayStart?.value === 'true'; } - ); + if (startLabel) { + this.startProcessButtonLabel = startLabel?.value?.trim()?.length > 0 ? startLabel.value.trim() : this.defaultStartProcessButtonLabel; + } + + if (displayCancel) { + this.displayCancelButton = displayCancel?.value === 'true'; + } + if (cancelLabel) { + this.cancelButtonLabel = cancelLabel?.value?.trim()?.length > 0 ? cancelLabel.value.trim() : this.defaultCancelProcessButtonLabel; + } + }); this.isFormCloudLoaded = false; this.processPayloadCloud.processDefinitionKey = processDefinitionCurrent.key; diff --git a/lib/process-services-cloud/src/lib/process/start-process/services/start-process-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/process/start-process/services/start-process-cloud.service.spec.ts index f10b6027475..2ba675349b2 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/services/start-process-cloud.service.spec.ts +++ b/lib/process-services-cloud/src/lib/process/start-process/services/start-process-cloud.service.spec.ts @@ -125,4 +125,29 @@ describe('StartProcessCloudService', () => { expect(requestSpy.calls.mostRecent().args[0]).toContain(`${appName}/rb/v1/process-definitions/${processDefinitionId}/static-values`); expect(requestSpy.calls.mostRecent().args[1].httpMethod).toBe('GET'); }); + + it('should transform the response into task variables when retrieving the constant values for the start event', async () => { + const appName = 'test-app'; + const processDefinitionId = 'processDefinitionId'; + const requestSpy = spyOn(adfHttpClient, 'request'); + requestSpy.and.returnValue(Promise.resolve({ constant1: 'value', constant2: '0', constant3: 'true' })); + + const result = await service.getStartEventConstants(appName, processDefinitionId).toPromise(); + + expect(result.length).toEqual(3); + expect(result[0].name).toEqual('constant1'); + expect(result[0].id).toEqual('constant1'); + expect(result[0].value).toEqual('value'); + expect(result[0].type).toEqual('string'); + expect(result[1].name).toEqual('constant2'); + expect(result[1].id).toEqual('constant2'); + expect(result[1].value).toEqual('0'); + expect(result[1].type).toEqual('string'); + expect(result[2].name).toEqual('constant3'); + expect(result[2].id).toEqual('constant3'); + expect(result[2].value).toEqual('true'); + expect(result[2].type).toEqual('string'); + expect(requestSpy.calls.mostRecent().args[0]).toContain(`${appName}/rb/v1/process-definitions/${processDefinitionId}/constant-values`); + expect(requestSpy.calls.mostRecent().args[1].httpMethod).toBe('GET'); + }); }); diff --git a/lib/process-services-cloud/src/lib/process/start-process/services/start-process-cloud.service.ts b/lib/process-services-cloud/src/lib/process/start-process/services/start-process-cloud.service.ts index d847f99e39f..37bdc98a989 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/services/start-process-cloud.service.ts +++ b/lib/process-services-cloud/src/lib/process/start-process/services/start-process-cloud.service.ts @@ -119,4 +119,26 @@ export class StartProcessCloudService extends BaseCloudService { }) ); } + + /** + * Gets the constants mapped to the start form of a process definition. + * + * @param appName Name of the app + * @param processDefinitionId ID of the target process definition + * @returns Constants values for the start event + */ + getStartEventConstants(appName: string, processDefinitionId: string): Observable { + const apiUrl = `${this.getBasePath(appName)}/rb/v1/process-definitions/${processDefinitionId}/constant-values`; + return this.get(apiUrl).pipe( + map((res: { [key: string]: any }) => { + const result = []; + if (res) { + Object.keys(res).forEach((constant) => + result.push(new TaskVariableCloud({ name: constant, value: res[constant], type: 'string' })) + ); + } + return result; + }) + ); + } } From dd2e15a66e2f44b59fad872e5d4f5ec9b8505333 Mon Sep 17 00:00:00 2001 From: Pablo Martinez Garcia Date: Mon, 26 Aug 2024 11:30:42 +0200 Subject: [PATCH 2/4] AAE-25190 Fix code duplucation --- .../start-process-cloud.component.spec.ts | 110 +++++++++--------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts index 37422956b1f..d2a1ea0844f 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts @@ -122,6 +122,56 @@ describe('StartProcessCloudComponent', () => { TestBed.resetTestingModule(); }); + /** + * Setup the component with the given start event information. + * @param values the values for the form + * @param staticValues the static values retrieved from the API for the form + * @param constantValues the constant values retrieved from the API for customising the buttons + * @returns the start and cancel buttons HTML elements + */ + function loadWithStartEventInformation( + values?: TaskVariableCloud[], + staticValues?: TaskVariableCloud[] | Error, + constantValues?: TaskVariableCloud[] | Error + ): { + startButton: any; + cancelButton: any; + } { + if (values) { + component.values = values; + } + if (staticValues) { + if (staticValues instanceof Error) { + getStartEventConstantSpy.and.returnValue(throwError(() => staticValues)); + } else { + getStartEventFormStaticValuesMappingSpy.and.returnValue(of(staticValues)); + } + } + if (constantValues) { + if (constantValues instanceof Error) { + getStartEventConstantSpy.and.returnValue(throwError(() => constantValues)); + } else { + getStartEventConstantSpy.and.returnValue(of(constantValues)); + } + } + + component.name = 'My new process'; + component.processDefinitionName = 'processwithoutform2'; + getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinitionWithoutForm(component.processDefinitionName))); + fixture.detectChanges(); + + const change = new SimpleChange(null, 'MyApp', true); + component.ngOnChanges({ appName: change }); + fixture.detectChanges(); + tick(550); + fixture.detectChanges(); + + const startButton = fixture.nativeElement.querySelector('#button-start'); + const cancelButton = fixture.nativeElement.querySelector('#cancel_process'); + + return { startButton, cancelButton }; + } + describe('start a process without start form', () => { beforeEach(() => { component.name = 'My formless new process'; @@ -199,17 +249,9 @@ describe('StartProcessCloudComponent', () => { new TaskVariableCloud({ name: 'static2', value: 0 }), new TaskVariableCloud({ name: 'static3', value: true }) ]; - component.name = 'My new process'; - component.processDefinitionName = 'processwithoutform2'; - component.values = values; - getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinitionWithoutForm(component.processDefinitionName))); - getStartEventFormStaticValuesMappingSpy.and.returnValue(of(staticInputs)); - fixture.detectChanges(); - const change = new SimpleChange(null, 'MyApp', true); - component.ngOnChanges({ appName: change }); - fixture.detectChanges(); - tick(550); + loadWithStartEventInformation(values, staticInputs); + expect(component.resolvedValues).toEqual(staticInputs.concat(values)); })); @@ -219,21 +261,8 @@ describe('StartProcessCloudComponent', () => { new TaskVariableCloud({ name: 'startEnabled', value: 'false' }), new TaskVariableCloud({ name: 'cancelEnabled', value: 'false' }) ]; - getStartEventConstantSpy.and.returnValue(of(constants)); - - component.name = 'My new process'; - component.processDefinitionName = 'processwithoutform2'; - getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinitionWithoutForm(component.processDefinitionName))); - - const change = new SimpleChange(null, 'MyApp', true); - component.ngOnChanges({ appName: change }); - fixture.detectChanges(); - tick(550); - fixture.detectChanges(); - - const startButton = fixture.nativeElement.querySelector('#button-start'); - const cancelButton = fixture.nativeElement.querySelector('#cancel_process'); + const { startButton, cancelButton } = loadWithStartEventInformation(null, null, constants); expect(startButton).toBeNull(); expect(cancelButton).toBeNull(); @@ -246,43 +275,14 @@ describe('StartProcessCloudComponent', () => { new TaskVariableCloud({ name: 'cancelEnabled', value: 'true' }), new TaskVariableCloud({ name: 'cancelLabel', value: 'Cancel' }) ]; - getStartEventConstantSpy.and.returnValue(of(constants)); - - component.name = 'My new process'; - component.processDefinitionName = 'processwithoutform2'; - getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinitionWithoutForm(component.processDefinitionName))); - fixture.detectChanges(); - - const change = new SimpleChange(null, 'MyApp', true); - component.ngOnChanges({ appName: change }); - fixture.detectChanges(); - tick(550); - - fixture.detectChanges(); - - const startButton = fixture.nativeElement.querySelector('#button-start'); - const cancelButton = fixture.nativeElement.querySelector('#cancel_process'); + const { startButton, cancelButton } = loadWithStartEventInformation(null, null, constants); expect(startButton.textContent?.trim()).toEqual('Start'); expect(cancelButton.textContent?.trim()).toEqual('Cancel'); })); it('should load with default values when retrieving the constants fails', fakeAsync(() => { - getStartEventConstantSpy.and.returnValue(throwError(() => new Error('test'))); - component.name = 'My new process'; - component.processDefinitionName = 'processwithoutform2'; - getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinitionWithoutForm(component.processDefinitionName))); - fixture.detectChanges(); - - const change = new SimpleChange(null, 'MyApp', true); - component.ngOnChanges({ appName: change }); - fixture.detectChanges(); - tick(550); - - fixture.detectChanges(); - - const startButton = fixture.nativeElement.querySelector('#button-start'); - const cancelButton = fixture.nativeElement.querySelector('#cancel_process'); + const { startButton, cancelButton } = loadWithStartEventInformation(null, null, new Error('test')); expect(startButton).not.toBeNull(); expect(cancelButton).not.toBeNull(); From c721288e7df9a31e5165b912821b1ee39aba3742 Mon Sep 17 00:00:00 2001 From: Pablo Martinez Garcia Date: Mon, 26 Aug 2024 13:05:03 +0200 Subject: [PATCH 3/4] AAE-25190 Address review comments --- .../components/start-process-cloud.component.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts index 750393f8792..9d082de1f07 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts @@ -186,10 +186,8 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy } constructor(private translateService: TranslationService) { - this.startProcessButtonLabel = this.translateService - .instant('ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.ACTION.START') - .toUpperCase(); - this.cancelButtonLabel = this.translateService.instant('ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.ACTION.CANCEL').toUpperCase(); + this.startProcessButtonLabel = this.defaultStartProcessButtonLabel; + this.cancelButtonLabel = this.defaultCancelProcessButtonLabel; } ngOnInit() { From a46a25f87f508b63710c882f7e8c20b7a35208a6 Mon Sep 17 00:00:00 2001 From: Pablo Martinez Garcia Date: Fri, 6 Sep 2024 11:48:06 +0200 Subject: [PATCH 4/4] AAE-25190 Address review comments --- .../components/start-process-cloud.component.html | 4 ++-- .../components/start-process-cloud.component.ts | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html index 97bd53ebf36..6e9b2c44c48 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html +++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html @@ -100,7 +100,7 @@