[ACTIVITI-3720] cloud form support for form variable mapping (#5044)

* universal form model

* parse variables correctly

* turn group model into interface

* remove console.log

* interface instead of class

* update form id type

* improved form variable parsing

* improved variable conversion

* fix cloud tests

* fix typings and code bugs
This commit is contained in:
Denys Vuika
2019-09-04 20:14:21 +01:00
committed by Eugenio Romano
parent 53dc5f0b91
commit 2360ccc6d5
19 changed files with 311 additions and 559 deletions

View File

@@ -21,12 +21,11 @@ import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testin
import { Observable, of, throwError } from 'rxjs';
import {
FormFieldModel, FormFieldTypes, FormService, FormOutcomeEvent, FormOutcomeModel, LogService, WidgetVisibilityService,
setupTestBed, AppConfigService, FormRenderingService
setupTestBed, AppConfigService, FormRenderingService, FormModel
} from '@alfresco/adf-core';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { FormCloudService } from '../services/form-cloud.service';
import { FormCloudComponent } from './form-cloud.component';
import { FormCloud } from '../models/form-cloud.model';
import { cloudFormMock, fakeCloudForm } from '../mocks/cloud-form.mock';
import { FormCloudRepresentation } from '../models/form-cloud-representation.model';
@@ -53,12 +52,12 @@ describe('FormCloudComponent', () => {
it('should check form', () => {
expect(formComponent.hasForm()).toBeFalsy();
formComponent.form = new FormCloud();
formComponent.form = new FormModel();
expect(formComponent.hasForm()).toBeTruthy();
});
it('should allow title if showTitle is true', () => {
const formModel = new FormCloud();
const formModel = new FormModel();
formComponent.form = formModel;
expect(formComponent.showTitle).toBeTruthy();
@@ -67,7 +66,7 @@ describe('FormCloudComponent', () => {
});
it('should not allow title if showTitle is false', () => {
const formModel = new FormCloud();
const formModel = new FormModel();
formComponent.form = formModel;
formComponent.showTitle = false;
@@ -84,14 +83,14 @@ describe('FormCloudComponent', () => {
});
it('should enable custom outcome buttons', () => {
const formModel = new FormCloud();
const formModel = new FormModel();
formComponent.form = formModel;
const outcome = new FormOutcomeModel(<any> formModel, { id: 'action1', name: 'Action 1' });
expect(formComponent.isOutcomeButtonVisible(outcome, formComponent.form.readOnly)).toBeTruthy();
});
it('should allow controlling [complete] button visibility', () => {
const formModel = new FormCloud();
const formModel = new FormModel();
formComponent.form = formModel;
const outcome = new FormOutcomeModel(<any> formModel, { id: '$save', name: FormOutcomeModel.SAVE_ACTION });
@@ -103,7 +102,7 @@ describe('FormCloudComponent', () => {
});
it('should show only [complete] button with readOnly form ', () => {
const formModel = new FormCloud();
const formModel = new FormModel();
formModel.readOnly = true;
formComponent.form = formModel;
const outcome = new FormOutcomeModel(<any> formModel, { id: '$complete', name: FormOutcomeModel.COMPLETE_ACTION });
@@ -113,7 +112,7 @@ describe('FormCloudComponent', () => {
});
it('should not show [save] button with readOnly form ', () => {
const formModel = new FormCloud();
const formModel = new FormModel();
formModel.readOnly = true;
formComponent.form = formModel;
const outcome = new FormOutcomeModel(<any> formModel, { id: '$save', name: FormOutcomeModel.SAVE_ACTION });
@@ -123,7 +122,7 @@ describe('FormCloudComponent', () => {
});
it('should show [custom-outcome] button with readOnly form and selected custom-outcome', () => {
const formModel = new FormCloud({ selectedOutcome: 'custom-outcome' });
const formModel = new FormModel({ selectedOutcome: 'custom-outcome' });
formModel.readOnly = true;
formComponent.form = formModel;
let outcome = new FormOutcomeModel(<any> formModel, { id: '$customoutome', name: 'custom-outcome' });
@@ -137,7 +136,7 @@ describe('FormCloudComponent', () => {
});
it('should allow controlling [save] button visibility', () => {
const formModel = new FormCloud();
const formModel = new FormModel();
formModel.readOnly = false;
formComponent.form = formModel;
const outcome = new FormOutcomeModel(<any> formModel, { id: '$save', name: FormOutcomeModel.COMPLETE_ACTION });
@@ -164,7 +163,7 @@ describe('FormCloudComponent', () => {
});
});
spyOn(formCloudService, 'getTaskVariables').and.returnValue(of({}));
spyOn(formCloudService, 'getTaskVariables').and.returnValue(of([]));
spyOn(formCloudService, 'getTask').and.callFake((currentTaskId) => {
return new Observable((observer) => {
observer.next({ formRepresentation: { taskId: currentTaskId } });
@@ -189,7 +188,7 @@ describe('FormCloudComponent', () => {
});
});
spyOn(formCloudService, 'getTaskVariables').and.returnValue(of({}));
spyOn(formCloudService, 'getTaskVariables').and.returnValue(of([]));
formComponent.appName = 'test-app';
formComponent.taskId = null;
@@ -239,7 +238,7 @@ describe('FormCloudComponent', () => {
it('should call the process storage to retrieve the folder with only the taskId', fakeAsync(() => {
spyOn(formCloudService, 'getTaskForm').and.returnValue(of(cloudFormMock));
spyOn(formCloudService, 'getTaskVariables').and.returnValue(of({list: { entries: []}}));
spyOn(formCloudService, 'getTaskVariables').and.returnValue(of([]));
spyOn(formCloudService, 'getProcessStorageFolderTask')
.and.returnValue( of({nodeId : '123', path: '/a/path/type', type: 'fakeType'}));
const taskId = '<task id>';
@@ -258,7 +257,7 @@ describe('FormCloudComponent', () => {
it('should call the process storage to retrieve the folder with taskId and processInstanceId', fakeAsync(() => {
spyOn(formCloudService, 'getTaskForm').and.returnValue(of(cloudFormMock));
spyOn(formCloudService, 'getTaskVariables').and.returnValue(of({list: { entries: []}}));
spyOn(formCloudService, 'getTaskVariables').and.returnValue(of([]));
spyOn(formCloudService, 'getProcessStorageFolderTask')
.and.returnValue( of({nodeId : '123', path: '/a/path/type', type: 'fakeType'}));
const taskId = '<task id>';
@@ -311,7 +310,7 @@ describe('FormCloudComponent', () => {
});
it('should complete form on custom outcome click', () => {
const formModel = new FormCloud();
const formModel = new FormModel();
const outcomeName = 'Custom Action';
const outcome = new FormOutcomeModel(<any> formModel, { id: 'custom1', name: outcomeName });
@@ -327,7 +326,7 @@ describe('FormCloudComponent', () => {
});
it('should save form on [save] outcome click', () => {
const formModel = new FormCloud();
const formModel = new FormModel();
const outcome = new FormOutcomeModel(<any> formModel, {
id: FormCloudComponent.SAVE_OUTCOME_ID,
name: 'Save',
@@ -343,7 +342,7 @@ describe('FormCloudComponent', () => {
});
it('should complete form on [complete] outcome click', () => {
const formModel = new FormCloud();
const formModel = new FormModel();
const outcome = new FormOutcomeModel(<any> formModel, {
id: FormCloudComponent.COMPLETE_OUTCOME_ID,
name: 'Complete',
@@ -359,7 +358,7 @@ describe('FormCloudComponent', () => {
});
it('should emit form saved event on custom outcome click', () => {
const formModel = new FormCloud();
const formModel = new FormModel();
const outcome = new FormOutcomeModel(<any> formModel, {
id: FormCloudComponent.CUSTOM_OUTCOME_ID,
name: 'Custom',
@@ -376,7 +375,7 @@ describe('FormCloudComponent', () => {
});
it('should do nothing when clicking outcome for readonly form', () => {
const formModel = new FormCloud();
const formModel = new FormModel();
const outcomeName = 'Custom Action';
const outcome = new FormOutcomeModel(<any> formModel, { id: 'custom1', name: outcomeName });
@@ -389,13 +388,13 @@ describe('FormCloudComponent', () => {
});
it('should require outcome model when clicking outcome', () => {
formComponent.form = new FormCloud();
formComponent.form = new FormModel();
formComponent.readOnly = false;
expect(formComponent.onOutcomeClicked(null)).toBeFalsy();
});
it('should require loaded form when clicking outcome', () => {
const formModel = new FormCloud();
const formModel = new FormModel();
const outcomeName = 'Custom Action';
const outcome = new FormOutcomeModel(<any> formModel, { id: 'custom1', name: outcomeName });
@@ -405,7 +404,7 @@ describe('FormCloudComponent', () => {
});
it('should not execute unknown system outcome', () => {
const formModel = new FormCloud();
const formModel = new FormModel();
const outcome = new FormOutcomeModel(<any> formModel, { id: 'unknown', name: 'Unknown', isSystem: true });
formComponent.form = formModel;
@@ -413,7 +412,7 @@ describe('FormCloudComponent', () => {
});
it('should require custom action name to complete form', () => {
const formModel = new FormCloud();
const formModel = new FormModel();
let outcome = new FormOutcomeModel(<any> formModel, { id: 'custom' });
formComponent.form = formModel;
@@ -429,7 +428,7 @@ describe('FormCloudComponent', () => {
const taskId = '456';
spyOn(formCloudService, 'getTask').and.returnValue(of({}));
spyOn(formCloudService, 'getTaskVariables').and.returnValue(of({}));
spyOn(formCloudService, 'getTaskVariables').and.returnValue(of([]));
spyOn(formCloudService, 'getTaskForm').and.returnValue(of({ taskId: taskId, selectedOutcome: 'custom-outcome' }));
formComponent.formLoaded.subscribe(() => {
@@ -448,7 +447,7 @@ describe('FormCloudComponent', () => {
const error = 'Some error';
spyOn(formCloudService, 'getTask').and.returnValue(of({}));
spyOn(formCloudService, 'getTaskVariables').and.returnValue(of({}));
spyOn(formCloudService, 'getTaskVariables').and.returnValue(of([]));
spyOn(formComponent, 'handleError').and.stub();
spyOn(formCloudService, 'getTaskForm').and.callFake(() => {
return throwError(error);
@@ -505,7 +504,7 @@ describe('FormCloudComponent', () => {
const appName = 'test-app';
const processInstanceId = '333-444';
const formModel = new FormCloud({
const formModel = new FormModel({
id: '23',
taskId: taskId,
fields: [
@@ -532,7 +531,7 @@ describe('FormCloudComponent', () => {
const taskId = '123-223';
const appName = 'test-app';
const formModel = new FormCloud({
const formModel = new FormModel({
id: '23',
taskId: taskId,
fields: [
@@ -555,7 +554,7 @@ describe('FormCloudComponent', () => {
formComponent.form = null;
formComponent.saveTaskForm();
formComponent.form = new FormCloud();
formComponent.form = new FormModel();
formComponent.appName = 'test-app';
formComponent.saveTaskForm();
@@ -573,7 +572,7 @@ describe('FormCloudComponent', () => {
formComponent.form = null;
formComponent.completeTaskForm('save');
formComponent.form = new FormCloud();
formComponent.form = new FormModel();
formComponent.appName = 'test-app';
formComponent.completeTaskForm('complete');
@@ -600,7 +599,7 @@ describe('FormCloudComponent', () => {
const appName = 'test-app';
const processInstanceId = '333-444';
const formModel = new FormCloud({
const formModel = new FormModel({
id: '23',
taskId: taskId,
fields: [
@@ -646,12 +645,12 @@ describe('FormCloudComponent', () => {
it('should prevent default outcome execution', () => {
const outcome = new FormOutcomeModel(<any> new FormCloud(), {
const outcome = new FormOutcomeModel(<any> new FormModel(), {
id: FormCloudComponent.CUSTOM_OUTCOME_ID,
name: 'Custom'
});
formComponent.form = new FormCloud();
formComponent.form = new FormModel();
formComponent.executeOutcome.subscribe((event: FormOutcomeEvent) => {
expect(event.outcome).toBe(outcome);
event.preventDefault();
@@ -663,12 +662,12 @@ describe('FormCloudComponent', () => {
});
it('should not prevent default outcome execution', () => {
const outcome = new FormOutcomeModel(<any> new FormCloud(), {
const outcome = new FormOutcomeModel(<any> new FormModel(), {
id: FormCloudComponent.CUSTOM_OUTCOME_ID,
name: 'Custom'
});
formComponent.form = new FormCloud();
formComponent.form = new FormModel();
formComponent.executeOutcome.subscribe((event: FormOutcomeEvent) => {
expect(event.outcome).toBe(outcome);
expect(event.defaultPrevented).toBeFalsy();
@@ -691,17 +690,17 @@ describe('FormCloudComponent', () => {
formComponent.checkVisibility(field);
expect(visibilityService.refreshVisibility).not.toHaveBeenCalled();
field = new FormFieldModel(<any> new FormCloud());
field = new FormFieldModel(<any> new FormModel());
formComponent.checkVisibility(field);
expect(visibilityService.refreshVisibility).toHaveBeenCalledWith(field.form);
});
it('should disable outcome buttons for readonly form', () => {
const formModel = new FormCloud();
const formModel = new FormModel();
formModel.readOnly = true;
formComponent.form = formModel;
const outcome = new FormOutcomeModel(<any> new FormCloud(), {
const outcome = new FormOutcomeModel(<any> new FormModel(), {
id: FormCloudComponent.CUSTOM_OUTCOME_ID,
name: 'Custom'
});
@@ -710,12 +709,12 @@ describe('FormCloudComponent', () => {
});
it('should require outcome to eval button state', () => {
formComponent.form = new FormCloud();
formComponent.form = new FormModel();
expect(formComponent.isOutcomeButtonEnabled(null)).toBeFalsy();
});
it('should disable complete outcome button when disableCompleteButton is true', () => {
const formModel = new FormCloud(cloudFormMock);
const formModel = new FormModel(cloudFormMock);
formComponent.form = formModel;
formComponent.disableCompleteButton = true;
@@ -730,7 +729,7 @@ describe('FormCloudComponent', () => {
});
it('should disable save outcome button when disableSaveButton is true', () => {
const formModel = new FormCloud(cloudFormMock);
const formModel = new FormModel(cloudFormMock);
formComponent.form = formModel;
formComponent.disableSaveButton = true;
@@ -745,7 +744,7 @@ describe('FormCloudComponent', () => {
});
it('should disable start process outcome button when disableStartProcessButton is true', () => {
const formModel = new FormCloud(cloudFormMock);
const formModel = new FormModel(cloudFormMock);
formComponent.form = formModel;
formComponent.disableStartProcessButton = true;
@@ -764,17 +763,17 @@ describe('FormCloudComponent', () => {
done();
});
const outcome = new FormOutcomeModel(<any> new FormCloud(), {
const outcome = new FormOutcomeModel(<any> new FormModel(), {
id: FormCloudComponent.CUSTOM_OUTCOME_ID,
name: 'Custom'
});
formComponent.form = new FormCloud();
formComponent.form = new FormModel();
formComponent.onOutcomeClicked(outcome);
});
it('should refresh form values when data is changed', (done) => {
formComponent.form = new FormCloud(JSON.parse(JSON.stringify(cloudFormMock)));
formComponent.form = new FormModel(JSON.parse(JSON.stringify(cloudFormMock)));
formComponent.formCloudRepresentationJSON = new FormCloudRepresentation(JSON.parse(JSON.stringify(cloudFormMock)));
let formFields = formComponent.form.getFormFields();
@@ -803,7 +802,7 @@ describe('FormCloudComponent', () => {
});
it('should refresh radio buttons value when id is given to data', () => {
formComponent.form = new FormCloud(JSON.parse(JSON.stringify(cloudFormMock)));
formComponent.form = new FormModel(JSON.parse(JSON.stringify(cloudFormMock)));
formComponent.formCloudRepresentationJSON = new FormCloudRepresentation(JSON.parse(JSON.stringify(cloudFormMock)));
let formFields = formComponent.form.getFormFields();
let radioFieldById = formFields.find((field) => field.id === 'radiobuttons1');

View File

@@ -32,14 +32,16 @@ import {
NotificationService,
FormRenderingService,
FORM_FIELD_VALIDATORS,
FormFieldValidator
FormFieldValidator,
FormValues,
FormModel
} from '@alfresco/adf-core';
import { FormCloudService } from '../services/form-cloud.service';
import { FormCloud } from '../models/form-cloud.model';
import { TaskVariableCloud } from '../models/task-variable-cloud.model';
import { DropdownCloudWidgetComponent } from './dropdown-cloud/dropdown-cloud.widget';
import { AttachFileCloudWidgetComponent } from './attach-file-cloud-widget/attach-file-cloud-widget.component';
import { DateCloudWidgetComponent } from './date-cloud/date-cloud.widget';
import { TaskDetailsCloudModel } from '../../task/start-task/models/task-details-cloud.model';
@Component({
selector: 'adf-cloud-form',
@@ -61,7 +63,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
/** Underlying form model instance. */
@Input()
form: FormCloud;
form: FormModel;
/** Task id to fetch corresponding form and values. */
@Input()
@@ -76,22 +78,22 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
/** Emitted when the form is submitted with the `Save` or custom outcomes. */
@Output()
formSaved: EventEmitter<FormCloud> = new EventEmitter<FormCloud>();
formSaved = new EventEmitter<FormModel>();
/** Emitted when the form is submitted with the `Complete` outcome. */
@Output()
formCompleted: EventEmitter<FormCloud> = new EventEmitter<FormCloud>();
formCompleted = new EventEmitter<FormModel>();
/** Emitted when the form is loaded or reloaded. */
@Output()
formLoaded: EventEmitter<FormCloud> = new EventEmitter<FormCloud>();
formLoaded = new EventEmitter<FormModel>();
/** Emitted when form values are refreshed due to a data property change. */
@Output()
formDataRefreshed: EventEmitter<FormCloud> = new EventEmitter<FormCloud>();
formDataRefreshed = new EventEmitter<FormModel>();
@Output()
formContentClicked: EventEmitter<string> = new EventEmitter<string>();
formContentClicked = new EventEmitter<string>();
protected subscriptions: Subscription[] = [];
nodeId: string;
@@ -162,24 +164,24 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
}
findProcessVariablesByTaskId(appName: string, taskId: string): Observable<any> {
findProcessVariablesByTaskId(appName: string, taskId: string): Observable<TaskVariableCloud[]> {
return this.formCloudService.getTask(appName, taskId).pipe(
switchMap((task: any) => {
switchMap(task => {
if (this.isAProcessTask(task)) {
return this.formCloudService.getTaskVariables(appName, taskId);
} else {
return of({});
return of([]);
}
})
);
}
isAProcessTask(taskRepresentation) {
isAProcessTask(taskRepresentation: TaskDetailsCloudModel): boolean {
return taskRepresentation.processDefinitionId && taskRepresentation.processDefinitionDeploymentId !== 'null';
}
getFormByTaskId(appName: string, taskId: string): Promise<FormCloud> {
return new Promise<FormCloud>(resolve => {
getFormByTaskId(appName: string, taskId: string): Promise<FormModel> {
return new Promise<FormModel>(resolve => {
forkJoin(this.formCloudService.getTaskForm(appName, taskId),
this.formCloudService.getTaskVariables(appName, taskId))
.pipe(takeUntil(this.onDestroy$))
@@ -255,7 +257,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
saveTaskForm() {
if (this.form && this.appName && this.taskId) {
this.formCloudService
.saveTaskForm(this.appName, this.taskId, this.processInstanceId, this.form.id, this.form.values)
.saveTaskForm(this.appName, this.taskId, this.processInstanceId, `${this.form.id}`, this.form.values)
.pipe(takeUntil(this.onDestroy$))
.subscribe(
() => {
@@ -269,7 +271,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
completeTaskForm(outcome?: string) {
if (this.form && this.appName && this.taskId) {
this.formCloudService
.completeTaskForm(this.appName, this.taskId, this.processInstanceId, this.form.id, this.form.values, outcome)
.completeTaskForm(this.appName, this.taskId, this.processInstanceId, `${this.form.id}`, this.form.values, outcome)
.pipe(takeUntil(this.onDestroy$))
.subscribe(
() => {
@@ -280,9 +282,14 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
}
}
parseForm(formCloudRepresentationJSON: any): FormCloud {
parseForm(formCloudRepresentationJSON: any): FormModel {
if (formCloudRepresentationJSON) {
const form = new FormCloud(formCloudRepresentationJSON, this.data, this.readOnly, this.formCloudService);
const formValues: FormValues = {};
(this.data || []).forEach(variable => {
formValues[variable.name] = variable.value;
});
const form = new FormModel(formCloudRepresentationJSON, formValues, this.readOnly);
if (!form || !form.fields.length) {
form.outcomes = this.getFormDefinitionOutcomes(form);
}
@@ -298,7 +305,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
* Get custom set of outcomes for a Form Definition.
* @param form Form definition model.
*/
getFormDefinitionOutcomes(form: FormCloud): FormOutcomeModel[] {
getFormDefinitionOutcomes(form: FormModel): FormOutcomeModel[] {
return [
new FormOutcomeModel(<any> form, { id: '$save', name: FormOutcomeModel.SAVE_ACTION, isSystem: true })
];
@@ -316,15 +323,15 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
this.onFormDataRefreshed(this.form);
}
protected onFormLoaded(form: FormCloud) {
protected onFormLoaded(form: FormModel) {
this.formLoaded.emit(form);
}
protected onFormDataRefreshed(form: FormCloud) {
protected onFormDataRefreshed(form: FormModel) {
this.formDataRefreshed.emit(form);
}
protected onTaskSaved(form: FormCloud) {
protected onTaskSaved(form: FormModel) {
this.formSaved.emit(form);
}
@@ -332,7 +339,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
this.handleError(error);
}
protected onTaskCompleted(form: FormCloud) {
protected onTaskCompleted(form: FormModel) {
this.formCompleted.emit(form);
}

View File

@@ -15,22 +15,14 @@
* limitations under the License.
*/
import { FormCloudService } from '../services/form-cloud.service';
import { FormCloud } from './form-cloud.model';
import { TabModel, FormFieldModel, ContainerModel, FormOutcomeModel, FormFieldTypes, AppConfigService } from '@alfresco/adf-core';
import { TabModel, FormFieldModel, ContainerModel, FormOutcomeModel, FormFieldTypes, FormModel } from '@alfresco/adf-core';
import { FormCloudRepresentation } from './form-cloud-representation.model';
describe('FormCloud', () => {
let formCloudService: FormCloudService;
beforeEach(() => {
formCloudService = new FormCloudService(null, new AppConfigService(null), null);
});
it('should store original json', () => {
const formRepresentation = {fields: []};
const form = new FormCloud(formRepresentation);
const form = new FormModel(formRepresentation);
expect(form.json).toEqual(formRepresentation);
});
@@ -41,7 +33,7 @@ describe('FormCloud', () => {
taskId: '<task-id>',
taskName: '<task-name>'
};
const form = new FormCloud(formRepresentation);
const form = new FormModel(formRepresentation);
Object.keys(formRepresentation).forEach((key) => {
expect(form[key]).toEqual(form[key]);
@@ -54,17 +46,17 @@ describe('FormCloud', () => {
name: '<name>',
formDefinition: {}
};
const form = new FormCloud(formRepresentation);
const form = new FormModel(formRepresentation);
expect(form.taskName).toBe(formRepresentation.name);
});
it('should set readonly state from params', () => {
const form = new FormCloud({}, null, true);
const form = new FormModel({}, null, true);
expect(form.readOnly).toBeTruthy();
});
it('should check tabs', () => {
const form = new FormCloud();
const form = new FormModel();
form.tabs = null;
expect(form.hasTabs()).toBeFalsy();
@@ -77,7 +69,7 @@ describe('FormCloud', () => {
});
it('should check fields', () => {
const form = new FormCloud();
const form = new FormModel();
form.fields = null;
expect(form.hasFields()).toBeFalsy();
@@ -91,7 +83,7 @@ describe('FormCloud', () => {
});
it('should check outcomes', () => {
const form = new FormCloud();
const form = new FormModel();
form.outcomes = null;
expect(form.hasOutcomes()).toBeFalsy();
@@ -111,7 +103,7 @@ describe('FormCloud', () => {
]
};
const form = new FormCloud(formRepresentation);
const form = new FormModel(formRepresentation);
expect(form.tabs.length).toBe(2);
expect(form.tabs[0].id).toBe('tab1');
expect(form.tabs[1].id).toBe('tab2');
@@ -131,7 +123,7 @@ describe('FormCloud', () => {
]
};
const form = new FormCloud(formRepresentation);
const form = new FormModel(formRepresentation);
expect(form.fields.length).toBe(2);
expect(form.fields[0].id).toBe('field1');
expect(form.fields[1].id).toBe('field2');
@@ -142,7 +134,7 @@ describe('FormCloud', () => {
fields: null
};
const form = new FormCloud(formRepresentation);
const form = new FormModel(formRepresentation);
expect(form.fields).toBeDefined();
expect(form.fields.length).toBe(0);
});
@@ -161,7 +153,7 @@ describe('FormCloud', () => {
]
};
const form = new FormCloud(formRepresentation);
const form = new FormModel(formRepresentation);
expect(form.tabs.length).toBe(2);
expect(form.fields.length).toBe(4);
@@ -182,16 +174,16 @@ describe('FormCloud', () => {
]
};
const form = new FormCloud(formRepresentation);
const form = new FormModel(formRepresentation);
expect(form.outcomes.length).toBe(3);
expect(form.outcomes[0].id).toBe(FormCloud.SAVE_OUTCOME);
expect(form.outcomes[0].id).toBe(FormModel.SAVE_OUTCOME);
expect(form.outcomes[0].isSystem).toBeTruthy();
expect(form.outcomes[1].id).toBe(FormCloud.COMPLETE_OUTCOME);
expect(form.outcomes[1].id).toBe(FormModel.COMPLETE_OUTCOME);
expect(form.outcomes[1].isSystem).toBeTruthy();
expect(form.outcomes[2].id).toBe(FormCloud.START_PROCESS_OUTCOME);
expect(form.outcomes[2].id).toBe(FormModel.START_PROCESS_OUTCOME);
expect(form.outcomes[2].isSystem).toBeTruthy();
});
@@ -199,7 +191,7 @@ describe('FormCloud', () => {
const formRepresentation = {
fields: null
};
const form = new FormCloud(formRepresentation);
const form = new FormModel(formRepresentation);
expect(form.outcomes.length).toBe(0);
});
@@ -213,10 +205,10 @@ describe('FormCloud', () => {
]
};
const form = new FormCloud(formRepresentation);
const form = new FormModel(formRepresentation);
expect(form.outcomes.length).toBe(2);
expect(form.outcomes[0].id).toBe(FormCloud.SAVE_OUTCOME);
expect(form.outcomes[0].id).toBe(FormModel.SAVE_OUTCOME);
expect(form.outcomes[0].isSystem).toBeTruthy();
expect(form.outcomes[1].id).toBe('custom-1');
@@ -224,7 +216,7 @@ describe('FormCloud', () => {
});
it('should get field by id', () => {
const form = new FormCloud({}, null, false, formCloudService);
const form = new FormModel({}, null, false);
const field: any = { id: 'field1' };
spyOn(form, 'getFormFields').and.returnValue([field]);

View File

@@ -1,212 +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 {
TabModel, FormWidgetModel, FormOutcomeModel, FormValues,
FormWidgetModelCache, FormFieldModel, ContainerModel, FormFieldTypes,
ValidateFormFieldEvent, FormFieldValidator, FormFieldTemplates, FormBaseModel, FORM_FIELD_VALIDATORS } from '@alfresco/adf-core';
import { FormCloudService } from '../services/form-cloud.service';
import { TaskVariableCloud } from './task-variable-cloud.model';
export class FormCloud extends FormBaseModel {
static SAVE_OUTCOME: string = '$save';
static COMPLETE_OUTCOME: string = '$complete';
static START_PROCESS_OUTCOME: string = '$startProcess';
readonly id: string;
nodeId: string;
contentHost: string;
readonly name: string;
readonly taskId: string;
readonly taskName: string;
readonly selectedOutcome: string;
readOnly: boolean;
processDefinitionId: any;
className: string;
values: FormValues = {};
tabs: TabModel[] = [];
fields: FormWidgetModel[] = [];
outcomes: FormOutcomeModel[] = [];
customFieldTemplates: FormFieldTemplates = {};
fieldValidators: FormFieldValidator[] = [...FORM_FIELD_VALIDATORS];
constructor(formCloudRepresentationJSON?: any, formData?: TaskVariableCloud[], readOnly: boolean = false, protected formService?: FormCloudService) {
super();
this.readOnly = readOnly;
if (formCloudRepresentationJSON) {
this.json = formCloudRepresentationJSON;
this.id = formCloudRepresentationJSON.id;
this.name = formCloudRepresentationJSON.name;
this.taskId = formCloudRepresentationJSON.taskId;
this.taskName = formCloudRepresentationJSON.taskName || formCloudRepresentationJSON.name;
this.processDefinitionId = formCloudRepresentationJSON.processDefinitionId;
this.selectedOutcome = formCloudRepresentationJSON.selectedOutcome || '';
const tabCache: FormWidgetModelCache<TabModel> = {};
this.tabs = (formCloudRepresentationJSON.tabs || []).map((t) => {
const model = new TabModel(<any> this, t);
tabCache[model.id] = model;
return model;
});
this.fields = this.parseRootFields(formCloudRepresentationJSON);
if (formData && formData.length > 0) {
this.loadData(formData);
}
for (let i = 0; i < this.fields.length; i++) {
const field = this.fields[i];
if (field.tab) {
const tab = tabCache[field.tab];
if (tab) {
tab.fields.push(field);
}
}
}
if (formCloudRepresentationJSON.fields) {
const saveOutcome = new FormOutcomeModel(<any> this, {
id: FormCloud.SAVE_OUTCOME,
name: 'SAVE',
isSystem: true
});
const completeOutcome = new FormOutcomeModel(<any> this, {
id: FormCloud.COMPLETE_OUTCOME,
name: 'COMPLETE',
isSystem: true
});
const startProcessOutcome = new FormOutcomeModel(<any> this, {
id: FormCloud.START_PROCESS_OUTCOME,
name: 'START PROCESS',
isSystem: true
});
const customOutcomes = (formCloudRepresentationJSON.outcomes || []).map((obj) => new FormOutcomeModel(<any> this, obj));
this.outcomes = [saveOutcome].concat(
customOutcomes.length > 0 ? customOutcomes : [completeOutcome, startProcessOutcome]
);
}
}
this.validateForm();
}
hasTabs(): boolean {
return this.tabs && this.tabs.length > 0;
}
hasFields(): boolean {
return this.fields && this.fields.length > 0;
}
hasOutcomes(): boolean {
return this.outcomes && this.outcomes.length > 0;
}
onFormFieldChanged(field: FormFieldModel) {
this.validateField(field);
}
validateForm() {
const errorsField: FormFieldModel[] = [];
const fields = this.getFormFields();
for (let i = 0; i < fields.length; i++) {
if (!fields[i].validate()) {
errorsField.push(fields[i]);
}
}
this.isValid = errorsField.length > 0 ? false : true;
}
/**
* Validates a specific form field, triggers form validation.
*
* @param field Form field to validate.
* @memberof FormCloud
*/
validateField(field: FormFieldModel) {
if (!field) {
return;
}
const validateFieldEvent = new ValidateFormFieldEvent(<any> this, field);
if (!validateFieldEvent.isValid) {
this.isValid = false;
return;
}
if (validateFieldEvent.defaultPrevented) {
return;
}
if (!field.validate()) {
this.isValid = false;
}
this.validateForm();
}
// Activiti supports 3 types of root fields: container|group|dynamic-table
private parseRootFields(json: any): FormWidgetModel[] {
let fields = [];
if (json.fields) {
fields = json.fields;
}
const formWidgetModel: FormWidgetModel[] = [];
for (const field of fields) {
if (field.type === FormFieldTypes.DISPLAY_VALUE) {
// workaround for dynamic table on a completed/readonly form
if (field.params) {
const originalField = field.params['field'];
if (originalField.type === FormFieldTypes.DYNAMIC_TABLE) {
formWidgetModel.push(new ContainerModel(new FormFieldModel(<any> this, field)));
}
}
} else {
formWidgetModel.push(new ContainerModel(new FormFieldModel(<any> this, field)));
}
}
return formWidgetModel;
}
// Loads external data and overrides field values
// Typically used when form definition and form data coming from different sources
private loadData(formData: TaskVariableCloud[]) {
for (const field of this.getFormFields()) {
const fieldValue = formData.find((value) => { return value.name === field.id; });
if (fieldValue) {
field.json.value = fieldValue.value;
field.value = field.parseValue(field.json);
}
}
}
}

View File

@@ -15,21 +15,10 @@
* limitations under the License.
*/
export class FormDefinitionSelectorCloudModel {
id: number;
name: string;
description: string;
version: string;
standAlone: string;
constructor(obj?: any) {
if (obj) {
this.id = obj.id || null;
this.name = obj.name || null;
this.description = obj.description || null;
this.version = obj.version || null;
this.standAlone = obj.standAlone || null;
}
}
export interface FormDefinitionSelectorCloudModel {
id?: number;
name?: string;
description?: string;
version?: string;
standAlone?: string;
}

View File

@@ -15,7 +15,6 @@
* limitations under the License.
*/
export * from './models/form-cloud.model';
export * from './models/task-variable-cloud.model';
export * from './models/form-definition-selector-cloud.model';

View File

@@ -16,12 +16,11 @@
*/
import { Injectable } from '@angular/core';
import { AlfrescoApiService, LogService, FormValues, AppConfigService, FormOutcomeModel, FormFieldOption } from '@alfresco/adf-core';
import { AlfrescoApiService, LogService, FormValues, AppConfigService, FormOutcomeModel, FormFieldOption, FormModel } from '@alfresco/adf-core';
import { throwError, Observable, from } from 'rxjs';
import { catchError, map, switchMap } from 'rxjs/operators';
import { TaskDetailsCloudModel } from '../../task/start-task/models/task-details-cloud.model';
import { SaveFormRepresentation, CompleteFormRepresentation } from '@alfresco/js-api';
import { FormCloud } from '../models/form-cloud.model';
import { TaskVariableCloud, ProcessStorageCloudModel } from '../models/task-variable-cloud.model';
import { BaseCloudService } from '../../services/base-cloud.service';
@@ -258,12 +257,17 @@ export class FormCloudService extends BaseCloudService {
* @param readOnly Toggles whether or not the form should be read-only
* @returns Form created from the JSON specification
*/
parseForm(json: any, data?: TaskVariableCloud[], readOnly: boolean = false): FormCloud {
parseForm(json: any, data?: TaskVariableCloud[], readOnly: boolean = false): FormModel {
if (json) {
const flattenForm = {...json.formRepresentation, ...json.formRepresentation.formDefinition};
delete flattenForm.formDefinition;
const form = new FormCloud(flattenForm, data, readOnly, this);
const formValues: FormValues = {};
(data || []).forEach(variable => {
formValues[variable.name] = variable.value;
});
const form = new FormModel(flattenForm, formValues, readOnly);
if (!json.fields) {
form.outcomes = [
new FormOutcomeModel(<any> form, {

View File

@@ -23,13 +23,13 @@ import {
import { ProcessInstanceCloud } from '../models/process-instance-cloud.model';
import { StartProcessCloudService } from '../services/start-process-cloud.service';
import { FormControl, Validators, FormGroup, AbstractControl, FormBuilder, ValidatorFn } from '@angular/forms';
import { FormModel } from '@alfresco/adf-core';
import { MatAutocompleteTrigger } from '@angular/material';
import { ProcessPayloadCloud } from '../models/process-payload-cloud.model';
import { debounceTime, takeUntil } from 'rxjs/operators';
import { ProcessDefinitionCloud } from '../models/process-definition-cloud.model';
import { Subject } from 'rxjs';
import { TaskVariableCloud } from '../../../form/models/task-variable-cloud.model';
import { FormCloud } from '../../../form/models/form-cloud.model';
@Component({
selector: 'adf-cloud-start-process',
@@ -74,15 +74,15 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy
/** Emitted when the process is successfully started. */
@Output()
success: EventEmitter<ProcessInstanceCloud> = new EventEmitter<ProcessInstanceCloud>();
success = new EventEmitter<ProcessInstanceCloud>();
/** Emitted when the starting process is cancelled */
@Output()
cancel: EventEmitter<ProcessInstanceCloud> = new EventEmitter<ProcessInstanceCloud>();
cancel = new EventEmitter<ProcessInstanceCloud>();
/** Emitted when an error occurs. */
@Output()
error: EventEmitter<ProcessInstanceCloud> = new EventEmitter<ProcessInstanceCloud>();
error = new EventEmitter<ProcessInstanceCloud>();
processDefinitionList: ProcessDefinitionCloud[] = [];
processDefinitionCurrent: ProcessDefinitionCloud;
@@ -92,8 +92,9 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy
filteredProcesses: ProcessDefinitionCloud[] = [];
isLoading = false;
isFormCloudLoaded = false;
formCloud: FormCloud;
formCloud: FormModel;
protected onDestroy$ = new Subject<boolean>();
constructor(private startProcessCloudService: StartProcessCloudService,
private formBuilder: FormBuilder) {
}
@@ -128,7 +129,7 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy
return this.processDefinitionCurrent && !!this.processDefinitionCurrent.formKey;
}
onFormLoaded(form: FormCloud) {
onFormLoaded(form: FormModel) {
this.isFormCloudLoaded = true;
this.formCloud = form;
}

View File

@@ -44,6 +44,7 @@ export class TaskDetailsCloudModel {
managerOfCandidateGroup: boolean;
memberOfCandidateGroup: boolean;
memberOfCandidateUsers: boolean;
processDefinitionDeploymentId?: string;
constructor(obj?: any) {
if (obj) {

View File

@@ -19,10 +19,9 @@ import {
Component, EventEmitter, Input, OnChanges,
Output, SimpleChanges
} from '@angular/core';
import { FormCloud } from '../../../form/models/form-cloud.model';
import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model';
import { TaskCloudService } from '../../services/task-cloud.service';
import { FormRenderingService, ContentLinkModel } from '@alfresco/adf-core';
import { FormRenderingService, FormModel, ContentLinkModel } from '@alfresco/adf-core';
import { AttachFileCloudWidgetComponent } from '../../../form/components/attach-file-cloud-widget/attach-file-cloud-widget.component';
import { DropdownCloudWidgetComponent } from '../../../form/components/dropdown-cloud/dropdown-cloud.widget';
import { DateCloudWidgetComponent } from '../../../form/components/date-cloud/date-cloud.widget';
@@ -64,31 +63,31 @@ export class TaskFormCloudComponent implements OnChanges {
/** Emitted when the form is saved. */
@Output()
formSaved: EventEmitter<FormCloud> = new EventEmitter<FormCloud>();
formSaved = new EventEmitter<FormModel>();
/** Emitted when the form is submitted with the `Complete` outcome. */
@Output()
formCompleted: EventEmitter<FormCloud> = new EventEmitter<FormCloud>();
formCompleted = new EventEmitter<FormModel>();
/** Emitted when the task is completed. */
@Output()
taskCompleted: EventEmitter<string> = new EventEmitter<string>();
taskCompleted = new EventEmitter<string>();
/** Emitted when the task is claimed. */
@Output()
taskClaimed: EventEmitter<string> = new EventEmitter<string>();
taskClaimed = new EventEmitter<string>();
/** Emitted when the task is unclaimed. */
@Output()
taskUnclaimed: EventEmitter<string> = new EventEmitter<string>();
taskUnclaimed = new EventEmitter<string>();
/** Emitted when the cancel button is clicked. */
@Output()
cancelClick: EventEmitter<string> = new EventEmitter<string>();
cancelClick = new EventEmitter<string>();
/** Emitted when any error occurs. */
@Output()
error: EventEmitter<any> = new EventEmitter<any>();
error = new EventEmitter<any>();
@Output()
formContentClicked: EventEmitter<ContentLinkModel> = new EventEmitter();
@@ -171,11 +170,11 @@ export class TaskFormCloudComponent implements OnChanges {
this.cancelClick.emit(this.taskId);
}
onFormSaved(form: FormCloud) {
onFormSaved(form: FormModel) {
this.formSaved.emit(form);
}
onFormCompleted(form: FormCloud) {
onFormCompleted(form: FormModel) {
this.formCompleted.emit(form);
this.taskCompleted.emit(this.taskId);
}