AAE-28806 Consume-dynamic-Drop-Down-Radio-Button-form-variables (#10442)

* [AAE-28806] Enable form variables in REST for RadioButton

* [AAE-28806] Enable form variables in REST for Dropdown widget

* [AAE-28806] removed debugging information

* [AAE-28806] Removed code repitition

* [AAE-28806] Made fields readonly

* [AAE-28806] fixed pipeline

---------

Co-authored-by: Andreas Philippi <90837097+andrphilippi@users.noreply.github.com>
This commit is contained in:
Michaela Kröber
2024-12-05 16:10:55 +01:00
committed by GitHub
parent bace3b9dcc
commit e6c2152add
7 changed files with 179 additions and 7 deletions

View File

@@ -555,11 +555,21 @@ describe('FormModel', () => {
expect(value).toBeUndefined(); expect(value).toBeUndefined();
}); });
it('should find a process variable by form variable name', () => { it('should find a process variable by full form variable name', () => {
const value = form.getProcessVariableValue('variables.name1'); const value = form.getProcessVariableValue('variables.name1');
expect(value).toBe('hello'); expect(value).toBe('hello');
}); });
it('should find a process variable by form variable name', () => {
const value = form.getProcessVariableValue('name1');
expect(value).toBe('hello');
});
it('should find default form variable by form variable name', () => {
const value = form.getProcessVariableValue('name2');
expect(value).toBe('29.09.2019T00:00:00.000Z');
});
it('should find a process variable by name', () => { it('should find a process variable by name', () => {
const value = form.getProcessVariableValue('booleanVar'); const value = form.getProcessVariableValue('booleanVar');
expect(value).toEqual(true); expect(value).toEqual(true);

View File

@@ -37,11 +37,13 @@ import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatSelectHarness } from '@angular/material/select/testing'; import { MatSelectHarness } from '@angular/material/select/testing';
import { DebugElement } from '@angular/core'; import { DebugElement } from '@angular/core';
import { FormUtilsService } from '../../../services/form-utils.service';
describe('DropdownCloudWidgetComponent', () => { describe('DropdownCloudWidgetComponent', () => {
let formService: FormService; let formService: FormService;
let widget: DropdownCloudWidgetComponent; let widget: DropdownCloudWidgetComponent;
let formCloudService: FormCloudService; let formCloudService: FormCloudService;
let formUtilsService: FormUtilsService;
let fixture: ComponentFixture<DropdownCloudWidgetComponent>; let fixture: ComponentFixture<DropdownCloudWidgetComponent>;
let element: HTMLElement; let element: HTMLElement;
let loader: HarnessLoader; let loader: HarnessLoader;
@@ -56,6 +58,7 @@ describe('DropdownCloudWidgetComponent', () => {
formService = TestBed.inject(FormService); formService = TestBed.inject(FormService);
formCloudService = TestBed.inject(FormCloudService); formCloudService = TestBed.inject(FormCloudService);
formUtilsService = TestBed.inject(FormUtilsService);
loader = TestbedHarnessEnvironment.loader(fixture); loader = TestbedHarnessEnvironment.loader(fixture);
}); });
@@ -63,7 +66,7 @@ describe('DropdownCloudWidgetComponent', () => {
describe('Simple Dropdown', () => { describe('Simple Dropdown', () => {
beforeEach(() => { beforeEach(() => {
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', readOnly: false }), { widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', readOnly: false, id: 'form-id' }), {
id: 'dropdown-id', id: 'dropdown-id',
name: 'date-name', name: 'date-name',
type: 'dropdown', type: 'dropdown',
@@ -91,6 +94,26 @@ describe('DropdownCloudWidgetComponent', () => {
expect(formCloudService.getRestWidgetData).toHaveBeenCalled(); expect(formCloudService.getRestWidgetData).toHaveBeenCalled();
}); });
it('should call getRestWidgetData with correct body parameters when body is empty', () => {
spyOn(formCloudService, 'getRestWidgetData').and.returnValue(of(fakeOptionList));
widget.field.optionType = 'rest';
widget.ngOnInit();
expect(formCloudService.getRestWidgetData).toHaveBeenCalledWith('form-id', 'dropdown-id', {});
});
it('should call getRestWidgetData with correct body parameters when variables are mapped', () => {
spyOn(formCloudService, 'getRestWidgetData').and.returnValue(of(fakeOptionList));
widget.field.optionType = 'rest';
const body = { var1: 'value1', var2: 'value2' };
spyOn(formUtilsService, 'getRestUrlVariablesMap').and.returnValue(body);
widget.ngOnInit();
expect(formUtilsService.getRestUrlVariablesMap).toHaveBeenCalledWith(widget.field.form, widget.field.restUrl, {});
expect(formCloudService.getRestWidgetData).toHaveBeenCalledWith('form-id', 'dropdown-id', body);
});
it('should select the default value when an option is chosen as default', async () => { it('should select the default value when an option is chosen as default', async () => {
widget.field.value = 'option_2'; widget.field.value = 'option_2';
@@ -623,6 +646,9 @@ describe('DropdownCloudWidgetComponent', () => {
await dropdown.open(); await dropdown.open();
const allOptions = await dropdown.getOptions(); const allOptions = await dropdown.getOptions();
expect(formCloudService.getRestWidgetData).toHaveBeenCalledWith('fake-form-id', 'child-dropdown-id', {
parentDropdown: 'mock-value'
});
expect(jsonDataSpy).toHaveBeenCalledWith('fake-form-id', 'child-dropdown-id', { parentDropdown: 'mock-value' }); expect(jsonDataSpy).toHaveBeenCalledWith('fake-form-id', 'child-dropdown-id', { parentDropdown: 'mock-value' });
expect(await (await allOptions[0].host()).getAttribute('id')).toEqual('LO'); expect(await (await allOptions[0].host()).getAttribute('id')).toEqual('LO');
expect(await allOptions[0].getText()).toEqual('LONDON'); expect(await allOptions[0].getText()).toEqual('LONDON');

View File

@@ -40,6 +40,7 @@ import { filter, map } from 'rxjs/operators';
import { TaskVariableCloud } from '../../../models/task-variable-cloud.model'; import { TaskVariableCloud } from '../../../models/task-variable-cloud.model';
import { FormCloudService } from '../../../services/form-cloud.service'; import { FormCloudService } from '../../../services/form-cloud.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormUtilsService } from '../../../services/form-utils.service';
export const DEFAULT_OPTION = { export const DEFAULT_OPTION = {
id: 'empty', id: 'empty',
@@ -69,8 +70,9 @@ export const HIDE_FILTER_LIMIT = 5;
}) })
export class DropdownCloudWidgetComponent extends WidgetComponent implements OnInit, ReactiveFormWidget { export class DropdownCloudWidgetComponent extends WidgetComponent implements OnInit, ReactiveFormWidget {
public formService = inject(FormService); public formService = inject(FormService);
private formCloudService = inject(FormCloudService); private readonly formCloudService = inject(FormCloudService);
private appConfig = inject(AppConfigService); private readonly appConfig = inject(AppConfigService);
private readonly formUtilsService = inject(FormUtilsService);
private destroyRef = inject(DestroyRef); private destroyRef = inject(DestroyRef);
typeId = 'DropdownCloudWidgetComponent'; typeId = 'DropdownCloudWidgetComponent';
@@ -364,7 +366,8 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
const parentWidgetId = this.linkedWidgetId; const parentWidgetId = this.linkedWidgetId;
bodyParam[parentWidgetId] = parentWidgetValue; bodyParam[parentWidgetId] = parentWidgetValue;
} }
return bodyParam;
return this.formUtilsService.getRestUrlVariablesMap(this.field.form, this.field.restUrl, bodyParam);
} }
private loadFieldOptionsForLinkedWidget() { private loadFieldOptionsForLinkedWidget() {

View File

@@ -24,11 +24,13 @@ import { of, throwError } from 'rxjs';
import { HarnessLoader } from '@angular/cdk/testing'; import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatRadioButtonHarness, MatRadioGroupHarness } from '@angular/material/radio/testing'; import { MatRadioButtonHarness, MatRadioGroupHarness } from '@angular/material/radio/testing';
import { FormUtilsService } from '../../../services/form-utils.service';
describe('RadioButtonsCloudWidgetComponent', () => { describe('RadioButtonsCloudWidgetComponent', () => {
let fixture: ComponentFixture<RadioButtonsCloudWidgetComponent>; let fixture: ComponentFixture<RadioButtonsCloudWidgetComponent>;
let widget: RadioButtonsCloudWidgetComponent; let widget: RadioButtonsCloudWidgetComponent;
let formCloudService: FormCloudService; let formCloudService: FormCloudService;
let formUtilsService: FormUtilsService;
let element: HTMLElement; let element: HTMLElement;
let loader: HarnessLoader; let loader: HarnessLoader;
const restOption: FormFieldOption[] = [ const restOption: FormFieldOption[] = [
@@ -47,6 +49,7 @@ describe('RadioButtonsCloudWidgetComponent', () => {
imports: [ProcessServiceCloudTestingModule] imports: [ProcessServiceCloudTestingModule]
}); });
formCloudService = TestBed.inject(FormCloudService); formCloudService = TestBed.inject(FormCloudService);
formUtilsService = TestBed.inject(FormUtilsService);
fixture = TestBed.createComponent(RadioButtonsCloudWidgetComponent); fixture = TestBed.createComponent(RadioButtonsCloudWidgetComponent);
widget = fixture.componentInstance; widget = fixture.componentInstance;
element = fixture.nativeElement; element = fixture.nativeElement;
@@ -134,6 +137,21 @@ describe('RadioButtonsCloudWidgetComponent', () => {
spyOn(formCloudService, 'getRestWidgetData').and.returnValue(of(restOption)); spyOn(formCloudService, 'getRestWidgetData').and.returnValue(of(restOption));
}); });
it('should call getRestWidgetData with correct parameters and update field options on success', () => {
const formId = 'form-id';
const fieldId = 'field-id';
widget.field = new FormFieldModel(new FormModel({ id: formId }), { id: fieldId, restUrl: '<url>' });
const body = { var1: 'value1', var2: 'value2' };
spyOn(formUtilsService, 'getRestUrlVariablesMap').and.returnValue(body);
spyOn(widget.field, 'updateForm');
widget.getValuesFromRestApi();
expect(formCloudService.getRestWidgetData).toHaveBeenCalledWith(formId, fieldId, body);
expect(widget.field.options).toEqual(restOption);
expect(widget.field.updateForm).toHaveBeenCalled();
});
describe('when widget is readonly', () => { describe('when widget is readonly', () => {
it('should call rest api when form is NOT readonly', () => { it('should call rest api when form is NOT readonly', () => {
widget.field = new FormFieldModel(new FormModel({}, undefined, false), getRadioButtonsWidgetConfig(true)); widget.field = new FormFieldModel(new FormModel({}, undefined, false), getRadioButtonsWidgetConfig(true));

View File

@@ -22,6 +22,7 @@ import { ErrorMessageModel, FormFieldOption, FormService, WidgetComponent } from
import { FormCloudService } from '../../../services/form-cloud.service'; import { FormCloudService } from '../../../services/form-cloud.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormUtilsService } from '../../../services/form-utils.service';
@Component({ @Component({
selector: 'radio-buttons-cloud-widget', selector: 'radio-buttons-cloud-widget',
@@ -46,7 +47,12 @@ export class RadioButtonsCloudWidgetComponent extends WidgetComponent implements
private readonly destroyRef = inject(DestroyRef); private readonly destroyRef = inject(DestroyRef);
constructor(public formService: FormService, private formCloudService: FormCloudService, private translateService: TranslateService) { constructor(
public formService: FormService,
private readonly formCloudService: FormCloudService,
private readonly translateService: TranslateService,
private readonly formUtilsService: FormUtilsService
) {
super(formService); super(formService);
} }
@@ -57,8 +63,10 @@ export class RadioButtonsCloudWidgetComponent extends WidgetComponent implements
} }
getValuesFromRestApi() { getValuesFromRestApi() {
const body = this.formUtilsService.getRestUrlVariablesMap(this.field.form, this.field.restUrl, {});
this.formCloudService this.formCloudService
.getRestWidgetData(this.field.form.id, this.field.id) .getRestWidgetData(this.field.form.id, this.field.id, body)
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe( .subscribe(
(result: FormFieldOption[]) => { (result: FormFieldOption[]) => {

View File

@@ -0,0 +1,75 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* 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 { TestBed } from '@angular/core/testing';
import { FormUtilsService } from './form-utils.service';
import { FormModel, FormVariableModel } from '@alfresco/adf-core';
describe('FormUtilsService', () => {
let service: FormUtilsService;
const variables: FormVariableModel[] = [
{ id: '1', name: 'var1', type: 'string', value: 'value1' },
{ id: '2', name: 'var2', type: 'string', value: 'value2' }
];
/**
* Test the getRestUrlVariablesMap method
*
* @param restUrl The rest URL for getRestUrlVariablesMap
* @param inputBody The input body for getRestUrlVariablesMap
* @param expected The expected result of getRestUrlVariablesMap
*/
function testRestUrlVariablesMap(restUrl: string, inputBody: { [key: string]: any }, expected: { [key: string]: any }) {
const formModel = new FormModel({ variables });
spyOn(formModel, 'getProcessVariableValue').and.callFake((name) => {
return variables.find((variable) => variable.name === name)?.value;
});
const result = service.getRestUrlVariablesMap(formModel, restUrl, inputBody);
expect(result).toEqual(expected);
}
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(FormUtilsService);
});
it('should return an empty map if there are no variables', () => {
const formModel = new FormModel({ variables: [] });
const restUrl = 'https://example.com/api';
const inputBody = {};
const result = service.getRestUrlVariablesMap(formModel, restUrl, inputBody);
expect(result).toEqual({});
});
it('should map variable values to the input body if they are present in the restUrl', () => {
testRestUrlVariablesMap('https://example.com/api?var1=${var1}&var2=${var2}', {}, { var1: 'value1', var2: 'value2' });
});
it('should not map variable values if they are not present in the restUrl', () => {
testRestUrlVariablesMap('https://example.com/api', {}, {});
});
it('should merge the mapped variables with the input body', () => {
testRestUrlVariablesMap(
'https://example.com/api?var1=${var1}',
{ existingKey: 'existingValue' },
{ existingKey: 'existingValue', var1: 'value1' }
);
});
});

View File

@@ -0,0 +1,32 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* 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 { Injectable } from '@angular/core';
import { FormModel, FormVariableModel } from '@alfresco/adf-core';
@Injectable({
providedIn: 'root'
})
export class FormUtilsService {
getRestUrlVariablesMap(formModel: FormModel, restUrl: string, inputBody: { [key: string]: any }) {
return formModel.variables.reduce((map: { [key: string]: any }, variable: FormVariableModel) => {
const variablePattern = new RegExp(`\\$\\{${variable.name}\\}`);
if (variablePattern.test(restUrl)) map[variable.name] = formModel.getProcessVariableValue(variable.name);
return map;
}, inputBody);
}
}