[AAE-16303] Handle nested property data source in data table (#8895)

This commit is contained in:
Tomasz Gnyp
2023-09-08 17:19:53 +02:00
committed by GitHub
parent a8db044092
commit ce881b7dcf
3 changed files with 117 additions and 50 deletions

View File

@@ -16,7 +16,7 @@
*/ */
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DataColumn, FormFieldModel, FormFieldTypes, FormModel, LogService } from '@alfresco/adf-core'; import { DataColumn, FormFieldModel, FormFieldTypes, FormModel, LogService, VariableConfig } from '@alfresco/adf-core';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { DataTableWidgetComponent } from './data-table.widget'; import { DataTableWidgetComponent } from './data-table.widget';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module'; import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
@@ -32,7 +32,9 @@ import {
mockJsonProcessVariables, mockJsonProcessVariables,
mockSchemaDefinition, mockSchemaDefinition,
mockJsonResponseEuropeCountriesData, mockJsonResponseEuropeCountriesData,
mockJsonResponseFormVariable mockJsonResponseFormVariable,
mockJsonNestedResponseFormVariable,
mockJsonNestedResponseEuropeCountriesData
} from '../../../mocks/data-table-widget.mock'; } from '../../../mocks/data-table-widget.mock';
describe('DataTableWidgetComponent', () => { describe('DataTableWidgetComponent', () => {
@@ -45,7 +47,7 @@ describe('DataTableWidgetComponent', () => {
const errorIcon: string = 'error_outline'; const errorIcon: string = 'error_outline';
const getDataVariable = ( const getDataVariable = (
variableName: string, variableConfig: VariableConfig,
schemaDefinition: DataColumn[], schemaDefinition: DataColumn[],
processVariables?: TaskVariableCloud[], processVariables?: TaskVariableCloud[],
variables?: TaskVariableCloud[] variables?: TaskVariableCloud[]
@@ -56,11 +58,19 @@ describe('DataTableWidgetComponent', () => {
type: FormFieldTypes.DATA_TABLE, type: FormFieldTypes.DATA_TABLE,
optionType: 'variable', optionType: 'variable',
schemaDefinition, schemaDefinition,
variableConfig: { variableConfig
variableName
}
}); });
const mockVariableConfig: VariableConfig = {
variableName: 'json-form-variable'
};
const checkDataTableErrorMessage = () => {
const failedErrorMsgElement = fixture.debugElement.query(By.css('.adf-data-table-widget-failed-message'));
expect(failedErrorMsgElement.nativeElement.textContent.trim()).toBe(errorIcon.concat('FORM.FIELD.DATA_TABLE_LOAD_FAILED'));
};
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
@@ -101,7 +111,7 @@ describe('DataTableWidgetComponent', () => {
}); });
it('should properly initialize column schema', () => { it('should properly initialize column schema', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, [], mockJsonFormVariable); widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], mockJsonFormVariable);
fixture.detectChanges(); fixture.detectChanges();
widget.dataSource.getColumns().forEach((column, index) => widget.dataSource.getColumns().forEach((column, index) =>
@@ -110,7 +120,7 @@ describe('DataTableWidgetComponent', () => {
}); });
it('should properly initialize data source with priority on the field value if process and form variables are provided', () => { it('should properly initialize data source with priority on the field value if process and form variables are provided', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, mockJsonProcessVariables, mockJsonFormVariable); widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, mockJsonProcessVariables, mockJsonFormVariable);
widget.field.value = mockAmericaCountriesData; widget.field.value = mockAmericaCountriesData;
fixture.detectChanges(); fixture.detectChanges();
@@ -121,7 +131,7 @@ describe('DataTableWidgetComponent', () => {
}); });
it('should properly initialize data source based on field value', () => { it('should properly initialize data source based on field value', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, [], []); widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], []);
widget.field.value = mockAmericaCountriesData; widget.field.value = mockAmericaCountriesData;
fixture.detectChanges(); fixture.detectChanges();
@@ -131,8 +141,8 @@ describe('DataTableWidgetComponent', () => {
expect(widget.dataSource.getRows()).toEqual(expectedData.getRows()); expect(widget.dataSource.getRows()).toEqual(expectedData.getRows());
}); });
it('should properly initialize json response data source based on field value', () => { it('should properly initialize default json response data source based on field value if path is NOT provided', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, [], []); widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], []);
widget.field.value = mockJsonResponseEuropeCountriesData; widget.field.value = mockJsonResponseEuropeCountriesData;
fixture.detectChanges(); fixture.detectChanges();
@@ -142,8 +152,29 @@ describe('DataTableWidgetComponent', () => {
expect(widget.dataSource.getRows()).toEqual(expectedData.getRows()); expect(widget.dataSource.getRows()).toEqual(expectedData.getRows());
}); });
it('should properly initialize json response data source based on variable', () => { it('should properly initialize default json response data source based on variable if path is NOT provided', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, [], mockJsonResponseFormVariable); widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], mockJsonResponseFormVariable);
fixture.detectChanges();
const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
expectedData.getRows().forEach(row => row.cssClass = '');
expect(widget.dataSource.getRows()).toEqual(expectedData.getRows());
});
it('should properly initialize json response data source based on field value if path is provided', () => {
widget.field = getDataVariable({ ...mockVariableConfig, optionsPath: 'response.my-data' }, mockSchemaDefinition, [], []);
widget.field.value = mockJsonNestedResponseEuropeCountriesData;
fixture.detectChanges();
const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
expectedData.getRows().forEach(row => row.cssClass = '');
expect(widget.dataSource.getRows()).toEqual(expectedData.getRows());
});
it('should properly initialize json response data source based on variable if path is provided', () => {
widget.field = getDataVariable({ ...mockVariableConfig, optionsPath: 'response.my-data' }, mockSchemaDefinition, [], mockJsonNestedResponseFormVariable);
fixture.detectChanges(); fixture.detectChanges();
const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition); const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
@@ -153,7 +184,7 @@ describe('DataTableWidgetComponent', () => {
}); });
it('should properly initialize data source based on form variable', () => { it('should properly initialize data source based on form variable', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, [], mockJsonFormVariable); widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], mockJsonFormVariable);
fixture.detectChanges(); fixture.detectChanges();
const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition); const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
@@ -163,7 +194,7 @@ describe('DataTableWidgetComponent', () => {
}); });
it('should properly initialize data source based on process variable', () => { it('should properly initialize data source based on process variable', () => {
widget.field = getDataVariable('json-variable', mockSchemaDefinition, mockJsonProcessVariables); widget.field = getDataVariable({ variableName: 'json-variable' }, mockSchemaDefinition, mockJsonProcessVariables);
fixture.detectChanges(); fixture.detectChanges();
const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition); const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
@@ -173,7 +204,7 @@ describe('DataTableWidgetComponent', () => {
}); });
it('should NOT display error if form is in preview state', () => { it('should NOT display error if form is in preview state', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, [], mockJsonFormVariableWithIncorrectData); widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], mockJsonFormVariableWithIncorrectData);
spyOn(formCloudService, 'getPreviewState').and.returnValue(true); spyOn(formCloudService, 'getPreviewState').and.returnValue(true);
fixture.detectChanges(); fixture.detectChanges();
@@ -185,7 +216,7 @@ describe('DataTableWidgetComponent', () => {
}); });
it('should NOT display data table with data source if form is in preview state', () => { it('should NOT display data table with data source if form is in preview state', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, [], mockJsonFormVariable); widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], mockJsonFormVariable);
spyOn(formCloudService, 'getPreviewState').and.returnValue(true); spyOn(formCloudService, 'getPreviewState').and.returnValue(true);
fixture.detectChanges(); fixture.detectChanges();
@@ -196,36 +227,48 @@ describe('DataTableWidgetComponent', () => {
expect(dataTable).toBeNull(); expect(dataTable).toBeNull();
}); });
it('should be able to display and log error if data source is not linked to every column', () => { it('should display and log error if data source is not linked to every column', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, [], mockJsonFormVariableWithIncorrectData); widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], mockJsonFormVariableWithIncorrectData);
fixture.detectChanges(); fixture.detectChanges();
const failedErrorMsgElement = fixture.debugElement.query(By.css('.adf-data-table-widget-failed-message')); checkDataTableErrorMessage();
expect(failedErrorMsgElement.nativeElement.textContent.trim()).toBe(errorIcon.concat('FORM.FIELD.DATA_TABLE_LOAD_FAILED'));
expect(logServiceSpy).toHaveBeenCalledWith('Data source has corrupted model or structure'); expect(logServiceSpy).toHaveBeenCalledWith('Data source has corrupted model or structure');
expect(widget.dataSource.getRows()).toEqual([]); expect(widget.dataSource.getRows()).toEqual([]);
}); });
it('should be able to display and log error if data source has invalid column structure', () => { it('should display and log error if data source has invalid column structure', () => {
widget.field = getDataVariable('json-form-variable', mockInvalidSchemaDefinition, [], mockJsonFormVariableWithIncorrectData); widget.field = getDataVariable(mockVariableConfig, mockInvalidSchemaDefinition, [], mockJsonFormVariableWithIncorrectData);
fixture.detectChanges(); fixture.detectChanges();
const failedErrorMsgElement = fixture.debugElement.query(By.css('.adf-data-table-widget-failed-message')); checkDataTableErrorMessage();
expect(failedErrorMsgElement.nativeElement.textContent.trim()).toBe(errorIcon.concat('FORM.FIELD.DATA_TABLE_LOAD_FAILED'));
expect(logServiceSpy).toHaveBeenCalledWith('Data source has corrupted model or structure'); expect(logServiceSpy).toHaveBeenCalledWith('Data source has corrupted model or structure');
expect(widget.dataSource.getRows()).toEqual([]); expect(widget.dataSource.getRows()).toEqual([]);
}); });
it('should be able to display and log error if data source is not found', () => { it('should display and log error if data source is not found', () => {
widget.field = getDataVariable('not-found-data-source', mockSchemaDefinition, [], mockJsonFormVariableWithIncorrectData); widget.field = getDataVariable({ variableName: 'not-found-data-source' }, mockSchemaDefinition, [], mockJsonFormVariableWithIncorrectData);
fixture.detectChanges(); fixture.detectChanges();
const failedErrorMsgElement = fixture.debugElement.query(By.css('.adf-data-table-widget-failed-message')); checkDataTableErrorMessage();
expect(logServiceSpy).toHaveBeenCalledWith('Data source not found or it is not an array');
expect(widget.dataSource).toBeUndefined();
});
expect(failedErrorMsgElement.nativeElement.textContent.trim()).toBe(errorIcon.concat('FORM.FIELD.DATA_TABLE_LOAD_FAILED')); it('should display and log error if path is incorrect', () => {
expect(logServiceSpy).toHaveBeenCalledWith('Data source not found'); widget.field = getDataVariable({ ...mockVariableConfig, optionsPath: 'wrong.path' }, mockSchemaDefinition, mockJsonNestedResponseFormVariable, []);
fixture.detectChanges();
checkDataTableErrorMessage();
expect(logServiceSpy).toHaveBeenCalledWith('Data source not found or it is not an array');
expect(widget.dataSource).toBeUndefined();
});
it('should display and log error if provided data by path is not an array', () => {
widget.field = getDataVariable({ ...mockVariableConfig, optionsPath: 'response.no-array' }, mockSchemaDefinition, mockJsonNestedResponseFormVariable, []);
fixture.detectChanges();
checkDataTableErrorMessage();
expect(logServiceSpy).toHaveBeenCalledWith('Data source not found or it is not an array');
expect(widget.dataSource).toBeUndefined(); expect(widget.dataSource).toBeUndefined();
}); });
}); });

View File

@@ -90,7 +90,7 @@ export class DataTableWidgetComponent extends WidgetComponent implements OnInit
} }
private initDataTable(): void { private initDataTable(): void {
if (this.rowsData) { if (this.rowsData?.length) {
this.dataSource = new WidgetDataTableAdapter(this.rowsData, this.columnsSchema); this.dataSource = new WidgetDataTableAdapter(this.rowsData, this.columnsSchema);
if (this.dataSource.isDataSourceValid()) { if (this.dataSource.isDataSourceValid()) {
@@ -99,16 +99,18 @@ export class DataTableWidgetComponent extends WidgetComponent implements OnInit
this.handleError('Data source has corrupted model or structure'); this.handleError('Data source has corrupted model or structure');
} }
} else { } else {
this.handleError('Data source not found'); this.handleError('Data source not found or it is not an array');
} }
} }
private getRowsData(): void { private getRowsData(): void {
const optionsPath = this.field?.variableConfig?.optionsPath ?? this.defaultResponseProperty;
const fieldValue = this.field?.value; const fieldValue = this.field?.value;
const rowsData = fieldValue ? fieldValue : this.getDataFromVariable(); const rowsData = fieldValue || this.getDataFromVariable();
if (rowsData) { if (rowsData) {
this.rowsData = rowsData[this.defaultResponseProperty] || rowsData as DataRow[]; const dataFromPath = this.getOptionsFromPath(rowsData, optionsPath);
this.rowsData = dataFromPath?.length ? dataFromPath : rowsData as DataRow[];
} }
} }
@@ -122,6 +124,23 @@ export class DataTableWidgetComponent extends WidgetComponent implements OnInit
return processVariableDropdownOptions ?? formVariableDropdownOptions; return processVariableDropdownOptions ?? formVariableDropdownOptions;
} }
private getOptionsFromPath(data: any, path: string): DataRow[] {
const properties = path.split('.');
const currentProperty = properties.shift();
if (!data.hasOwnProperty(currentProperty)) {
return [];
}
const nestedData = data[currentProperty];
if (Array.isArray(nestedData)) {
return nestedData;
}
return this.getOptionsFromPath(nestedData, properties.join('.'));
}
private getVariableValueByName(variables: TaskVariableCloud[], variableName: string): any { private getVariableValueByName(variables: TaskVariableCloud[], variableName: string): any {
return variables?.find((variable: TaskVariableCloud) => variable?.name === `variables.${variableName}` || variable?.name === variableName)?.value; return variables?.find((variable: TaskVariableCloud) => variable?.name === `variables.${variableName}` || variable?.name === variableName)?.value;
} }

View File

@@ -85,20 +85,21 @@ export const mockEuropeCountriesData = [
]; ];
export const mockJsonResponseEuropeCountriesData = { export const mockJsonResponseEuropeCountriesData = {
data: [ data: mockEuropeCountriesData
{ };
id: 'PL',
name: 'Poland' export const mockJsonNestedResponseEuropeCountriesData = {
}, response: {
{ empty: [],
id: 'IT', 'my-data': mockEuropeCountriesData,
name: 'Italy' data: [
}, {
{ id: 'HR',
id: 'UK', name: 'Croatia'
name: 'United Kingdom' }
} ],
] 'no-array': {}
}
}; };
export const mockAmericaCountriesData = [ export const mockAmericaCountriesData = [
@@ -137,6 +138,10 @@ export const mockJsonResponseFormVariable = [
new TaskVariableCloud({ name: 'json-form-variable', value: mockJsonResponseEuropeCountriesData, type: 'json', id: 'fake-id-1' }) new TaskVariableCloud({ name: 'json-form-variable', value: mockJsonResponseEuropeCountriesData, type: 'json', id: 'fake-id-1' })
]; ];
export const mockJsonNestedResponseFormVariable = [
new TaskVariableCloud({ name: 'json-form-variable', value: mockJsonNestedResponseEuropeCountriesData, type: 'json', id: 'fake-id-1' })
];
export const mockJsonProcessVariables = [ export const mockJsonProcessVariables = [
new TaskVariableCloud({ name: 'variables.json-variable', value: mockEuropeCountriesData, type: 'json', id: 'fake-id-1' }), new TaskVariableCloud({ name: 'variables.json-variable', value: mockEuropeCountriesData, type: 'json', id: 'fake-id-1' }),
new TaskVariableCloud({ name: 'variables.different-variable', value: 'fake-value', type: 'json', id: 'fake-id-2' }) new TaskVariableCloud({ name: 'variables.different-variable', value: 'fake-value', type: 'json', id: 'fake-id-2' })